JSONの構造と設定の役割
クライアント設定とコア設定の違い
JSON設定が定義するのは、プロキシコアが接続を受け付け、出口を選び、ドメインを名前解決し、セッションを管理する方法です。クライアントの設定すべてを表すものではありません。v2rayNが保存するウィンドウの状態、サブスクリプションのグループ、システムプロキシの切り替え、更新間隔などは、通常クライアント側のデータです。コアの起動時に、クライアントはそれらのデータをもとに実行用設定を生成します。v2rayNGとv2flyNGにも、それぞれ異なる設定管理方法があります。まず、確認しているファイルがクライアントの保存データ、エクスポートしたカスタム設定、今回の起動で実際に使われるファイルのどれなのかを確認してください。種類を取り違えると、正しく編集しても実行時に反映されないことがあります。
コア設定のトップレベルには通常、log、inbounds、outbounds、routing、dns、policyが含まれます。inboundsとoutboundsは配列で、複数のオブジェクトを定義できます。routingとpolicyはオブジェクトで、その中にルールやレベルをネストします。ファイル内の記述順が、そのまま実行順になるわけではありません。動作に影響するのは配列の順序や参照関係です。たとえばルーティングルールは上から順に照合され、どのルールにも一致しない接続は通常、outbounds配列の先頭に渡されます。
読みやすい最小構成を作る
以下は、ローカルで直接接続するための完全なテスト設定です。JSONの読み込み、ループバックアドレスでの待ち受け、ローカルから接続先へ直接アクセスできるかを確認できます。SOCKSのinboundとdirectのoutboundだけを定義しており、リモートプロキシ機能やシステムプロキシ設定はありません。このSOCKS入口を明示的に使うアプリの通信だけがコアに入ります。テスト前に、同じポートを使用している別のインスタンスを停止してください。ポートの競合を設定項目の誤りと取り違えるおそれがあります。
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "local-socks",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": []
}
}
設定を読むときは、まず論理的な流れを図にすると理解しやすくなります。アプリの接続がタグ付きinboundに入り、ルーティングが接続先とinboundのタグを確認し、最後に選択したoutboundへ渡します。名前解決が必要な場合はDNSが関わり、ポリシーはハンドシェイクやアイドル接続などの動作を制御します。この考え方は、項目を一つずつ暗記するより役立ちます。同じ問題でも原因の層は異なるためです。たとえば、アプリの通信がinboundに届いていないなら、outboundのプロトコルを変更しても結果は変わりません。outboundのタグを間違えている場合、DNSを変更しても参照エラーは解消しません。
構文、統合、変更範囲
このリファレンスでは標準JSONを使います。キーと文字列は二重引用符で囲み、真偽値はtrueまたはfalse、ポート番号は数値で記述します。配列やオブジェクトの最後の項目の後ろにカンマは付けません。コメントや拡張構文に対応するローダーもありますが、すべてのクライアントで使えるとは限りません。特に重複キーは避けてください。パーサーによって上書き、拒否、気付きにくい差異が生じる場合があります。設定を二つ統合するときは、ルートオブジェクトを続けて貼り付けるのではなく、同じトップレベルオブジェクトに項目をまとめます。
変更前に、正常に動作する設定を基準として保存し、どの画面や手順から変更したかを記録してください。GUIで管理している設定は、対応する設定画面から変更するのが基本です。自動生成ファイルを手作業で編集すると、再起動、ノード切り替え、サブスクリプション更新の際に上書きされることがあります。カスタム項目を継続して使う場合は、一時的なファイルパスに頼らず、クライアントのカスタム設定モード、上書きルール、ルーティングのインポート機能を確認してください。データの保存先はOSによって異なるため、他の人のスクリーンショットにある絶対パスをそのまま使うのは避けましょう。
コアの選択も、設定を読むうえでの前提です。V2FlyとXrayでは、一部のプロトコル名、トランスポート名、拡張項目が異なります。同じJSON形式だからといって、相互に使えるとは限りません。まずクライアントで現在のコアを確認し、そのコアが認識する項目と照らし合わせてください。未知の項目やプロトコルのエラーが出たら、設定全体を置き換えず、該当オブジェクトに絞って確認しましょう。用語は用語集を、操作手順は各ガイドをご覧ください。
inbounds:接続の受け付けとローカル待ち受け
待ち受けアドレス、ポート、プロトコル
inboundsは、コアが接続を受け付ける入口を定義します。入口ごとに、プロトコルと関連設定を指定する必要があります。ローカルでよく使われる入口はSOCKSとHTTPです。listenで待ち受けアドレス、portでポート、tagでルーティングから参照する名前を指定します。タグは設定内で使う識別子にすぎず、システムプロキシを自動で設定したり、ブラウザーから自動接続させたりするものではありません。アプリがプロキシを使うかどうかは、アプリ自身のプロキシ設定、OSの設定、その他の通信取り込み方法によって決まります。
ローカルでのみ使う場合は、待ち受けアドレスを127.0.0.1にすると、IPv4のループバックインターフェースに限定できます。0.0.0.0で待ち受けると、ローカルのすべてのIPv4インターフェースからの接続を受け付けます。LAN内の端末からアクセスできるかはファイアウォールやネットワーク環境にも左右されますが、公開範囲が広がることに変わりはありません。トラブル対処のためだけに待ち受け範囲を広げないでください。LANで共有する必要がある場合は、認証、接続を許可する送信元、OSのファイアウォールをあわせて設定し、誰がその出口を利用できるかを明確にしましょう。
ポート番号は、接続するアプリの設定と一致させる必要があります。SOCKSとHTTPは異なる接続プロトコルです。ポート番号が近いからといって、互換性があるわけではありません。以下は、二つのプロトコルを個別に試すためのinbound設定例です。ポート番号は一例であり、すべてのクライアントの既定値を示すものではありません。クライアントが混合型の入口を使う場合は、実際に生成された設定に従い、同じ待ち受けを重複して追加しないでください。
{
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
},
{
"tag": "http-in",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
]
}
UDPと接続先の識別
SOCKS設定のudpは、その入口で対応するUDPリクエストを処理できるようにします。ただし、すべてのアプリでUDPプロキシが自動的に使えるようになる一括切り替えではありません。アプリが対応する接続方法を使い、outboundのプロトコルとリモート側もその通信を転送できる必要があります。OSの一般的なHTTPプロキシ設定は、端末全体のUDP通信を取り込む設定とは異なります。Webページは開けるのにリアルタイム通信が失敗する場合は、この真偽値を何度も切り替えるのではなく、アプリからの接続、inboundの対応状況、出口の転送能力をそれぞれ確認してください。
sniffingは、接続の初期データから一部の接続先情報を識別する機能です。一般的な設定ではdestOverrideにHTTPのホスト名やTLSのサービス名を指定し、元の接続先がIPアドレスだけの場合にドメインルーティングを補助します。すべての接続から情報を識別できるわけではなく、任意の暗号化通信を復号する機能でもありません。識別可能な情報をプロトコルが含むか、クライアントでどのオプションが有効かによって結果は変わります。識別を有効にすれば、すべてのドメインルールに必ず一致するわけではありません。
{
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
上記のオブジェクトは、設定のルートではなく対象のinbound内に記述します。識別結果で接続先を書き換えるか、ルーティングにだけ使うかは、コアによって追加オプションや動作が異なります。元のIP接続の扱いを維持したい場合は、現在のコアが対応する「ルーティングのみ」のオプションを確認してください。また、識別機能は適切なDNS設計の代わりにはなりません。アプリがSOCKSにドメイン名を渡している場合は、もともとドメイン名を取得できることがあり、ルーティングのために識別へ頼る必要はありません。
待ち受けに失敗したときの確認手順
起動時にアドレス使用中のエラーが出たら、まず別のクライアントや古いコアが残っていないか確認し、次に今回の設定で同じアドレスとポートを重複して定義していないか調べます。待ち受けポートを変更した場合は、ブラウザーの手動プロキシ、アプリのプロキシ設定、クライアントが設定したシステムプロキシもあわせて変更してください。JSONの数字だけを変えても、アプリが古いポートに接続し続け、コアが応答しないように見えることがあります。詳しくはローカルポートの競合と待ち受けポートの変更をご覧ください。
起動エラーがないのにアクセス記録が見つからない場合は、アプリが今回のinboundに接続しているか、アプリ独自のプロキシ除外リストが設定されていないかを確認してください。また、IPv4とIPv6のループバックアドレスを区別し、どちらか一方で待ち受ければ両方に対応できると考えないでください。トラブル対処中は、入口を一つ、テスト用アプリを一つに絞ると、各リクエストの送信元を特定しやすくなります。入口が正常だと確認できてから、ほかの入口や複雑なルーティングを戻しましょう。複数のネットワーク層を同時に変更するより、原因を絞り込みやすくなります。
outbounds:出口とタグの参照
プロトコル、出口、既定の動作
outboundsは、通信をコアの外へ送る方法を定義します。直接接続には通常freedom、接続拒否には通常blackholeを使い、リモートプロキシには対応するプロトコルのoutboundを使います。protocolによってsettingsの構造が決まるため、プロトコル名だけを置き換え、別のプロトコル用サーバー項目を流用することはできません。ルーティングはoutboundTagからoutboundのtagを参照します。タグは一意で分かりやすい名前にし、変更したら参照先も更新してください。
ルーティングに一致しない通信は通常、outbounds配列の先頭に渡されるため、配列の順序には意味があります。directを先頭に移すと、一致しない通信の出口が変わります。拒否用のoutboundを先頭に置くと、多くの接続がすぐに失敗する可能性があります。outbounds配列を自動で最適な出口を選ぶ候補一覧のように扱わないでください。通常のoutboundオブジェクトを並べても、失敗時の切り替え、遅延テスト、負荷分散は行われません。これらの機能には、別の仕組みと明示的な設定が必要です。
{
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
]
}
上記の例は二つの出口を定義するだけで、どのリクエストを拒否するかは指定していません。ルーティングからblockを参照した場合に限り、該当する通信が拒否用outboundに送られます。ルーティングの動作確認には使えますが、リモートサービスの設定の代わりにはなりません。リモートプロキシのオブジェクトは、アドレス、ポート、ユーザー認証、トランスポート、セキュリティの設定など、サービス提供元の情報と一致させる必要があります。許可された情報源から設定を取得してください。別プロトコルの例から推測したり、公開のトラブル相談にそのまま載せたりしないでください。
ノード情報とコアの対応状況
VMess、VLESS、Trojan、Shadowsocksでは、認証項目やセキュリティモデルが異なります。サブスクリプションをインポートする利点の一つは、クライアントが共有形式を対応する内部形式に変換し、コア設定を生成してくれることです。変換できても、すべての項目が実行条件を満たしているとは限りません。プロトコルやトランスポートによって特定のコアが必要な場合や、クライアントが認識しない拡張パラメーターを含むリンクもあります。コアを切り替える前に、対象プロトコルへの対応状況を確認し、生成された設定と起動ログを確認してください。
リモートアドレスは接続先を指定し、TLSのサービス名は接続先の認証に使われます。両者が異なる場合もありますが、任意に置き換えることはできません。ポート番号だけでプロトコルの種類は決まりません。同じ番号のポートで異なるサービスが動作することもあります。接続タイムアウトが認証エラーとは限らず、認証拒否が明確なパスワードエラーとして表示されるとも限りません。サービスによっては接続を閉じるだけの場合もあります。アドレスへの到達、トランスポートの確立、セキュアなハンドシェイク、プロトコル認証の順に確認すると、すべての失敗をノードの停止と決めつけずに済みます。
| 項目 | 役割 | 確認ポイント |
|---|---|---|
tag | outboundのタグ | 一意で、ルーティング側の参照と一致している |
protocol | 出口プロトコルの指定 | 現在のコアが対応しているか |
settings | プロトコル固有のパラメーター | プロトコルに対応した構造か |
streamSettings | トランスポートとセキュリティの設定 | リモートサービスの設定と一致しているか |
directの名前解決とoutboundの再利用
direct接続でもDNSが必要になる場合があります。ルーティングでdirectが選ばれても、接続先がドメイン名なら、接続を確立するためにアドレスの取得が必要です。ルーティング段階のdomainStrategyとdirect outboundの名前解決オプションは、適用される段階が異なります。名前が似ていても同じ設定とは限りません。アドレスファミリーや名前解決の経路を制御したい場合は、DNSモジュール、ルーティング、direct outboundの設定を個別に確認し、最終的なページが開けるかだけでなく、ログで実際の接続先アドレスを確認してください。
接続の多重化は、すべてのプロトコルやアプリに適した汎用の高速化設定ではありません。複数の論理接続が一つの基盤接続を共有する方法を変えるため、障害の現れ方にも影響することがあります。特定のサイトが途切れる、長時間接続に問題が起きる、トランスポートの互換性に問題がある場合は、まず既定の多重化設定で基準を作ってから、個別に比較してください。多重化、トランスポート、セキュリティ検証、ルーティングを同時に変更すると、改善しても原因を特定できません。
ノードを変更しても、ルーティングのタグ設計は一貫性を保ってください。クライアントは選択中のノードに共通の出口タグを付ける場合があり、その場合はノード名が変わってもルールを変更せずに済みます。手動設定では、参照先を自分で管理する必要があります。外部ルールをインポートする前に、ローカル設定のoutbound名と照合し、存在しないタグを参照していないか確認してください。遅延、地域、プロトコルによるノードの違いは、ノードの選び方とテスト指標をご覧ください。どのノードを選ぶ場合も、設定内の参照が正しいことが前提です。
streamSettings:トランスポートとセキュリティの設定
プロキシプロトコルと通信方式を分けて考える
プロキシプロトコルは認証と通信の転送方法、トランスポート層は接続の運び方、セキュリティ層は接続の保護と認証をそれぞれ定めます。この三つは同じ設定項目ではありません。VLESSとREALITYは同じものではなく、WebSocketも独立したプロキシ認証プロトコルではありません。同じプロキシプロトコルでも異なるトランスポートと組み合わせられる一方、セキュリティ方式によっては対応するコアや組み合わせが限られます。設定を読むときは三つの層を分けると、共有リンクの各パラメーターをどのオブジェクトに設定するか判断しやすくなります。
streamSettingsは、対応するトランスポートが必要なinboundまたはoutboundのオブジェクト内に置くのが一般的です。networkでトランスポートの種類を、securityで接続の保護方式を指定し、それぞれの設定オブジェクトに詳細を記述します。これだけでリモート接続を確立できるわけではなく、認証情報の不足を補うものでもありません。以下は従来のTCP表記を使ったTLSの部分例です。現在のコアがこの表記を受け付け、サーバーが対応する通信方式を提供している場合にのみ使ってください。コアを確認せずに貼り付けないでください。
{
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "example.org",
"allowInsecure": false
}
}
}
例にあるexample.orgは説明用のドメインであり、プロキシ接続先として使えるサービスアドレスではありません。実際に設定するサービス名は、利用が許可されたサービスから提供された証明書の識別情報や設定要件と一致させてください。コアのドキュメントで別のトランスポート名や設定構造が指定されている場合は、その形式に従います。似て見える名前に文字列を置き換えるだけでは不十分です。互換性はクライアント画面の翻訳ではなく、実際に動作するコアの設定仕様に基づいて確認してください。
TLSのサービス名と証明書の検証
serverNameはTLSでのサービス識別に使われ、証明書の名前やサーバー側の選択に関わります。リモート接続先はIPアドレスでもドメイン名でも構いませんが、IPアドレスで接続する場合も、想定どおりのハンドシェイクを行うために正しいサービス名が必要なことがあります。逆に、よく見かけるドメイン名を適当に入力しても、証明書が有効になるわけではありません。証明書名の不一致、証明書チェーンの不備、システム時刻の誤り、中間機器による干渉などが、セキュアなハンドシェイクの失敗につながります。
allowInsecureを既定の修正手段にしないでください。証明書の検証を無効にすると、表面上のエラーが消えることはありますが、同時にセキュリティ上の境界も変わります。まずシステムの日付、サービス名、証明書の有効期限、信頼チェーンを確認し、ネットワーク上に証明書を差し替える機器がないか調べてください。サービスが独自の信頼体系を使う場合は、管理手順に沿って信頼済み証明書を設定し、異常を一律に検証スキップへ置き換えないでください。サブスクリプション更新時の証明書エラーと、ノードのハンドシェイク時の証明書エラーは分けて記録しましょう。
WebSocket、パス、途中の転送設定
WebSocketを使う場合、パス、リクエストのホスト名、リバースプロキシの転送ルールは、通常互いに一致させる必要があります。TLSのサービス名、HTTPリクエストのホスト項目、基盤となる接続先アドレスは、それぞれ役割が異なります。ある環境で同じ値が使われていても、互いに置き換えられるわけではありません。パスの大文字・小文字、末尾のスラッシュ、中間プロキシによる書き換えが原因で、リクエストが目的のサービスではなく通常のWebページに届くことがあります。この場合、従来のプロキシ認証エラーではなく、HTTPステータスエラーとして現れる場合があります。
トランスポートの問題を調べるときは、まずサービス提供元が指定する通信方式を確認し、TLSの要否、リクエストパス、ホスト情報を確認してください。ブラウザーでWebサイトが開けることを、プロキシのトランスポートが使える証拠にしないでください。通常のWebアクセスと特定のアップグレードリクエストでは、サーバー側の処理経路が異なる場合があります。クライアントにノードをインポートしている場合は、まず画面に保存された設定が揃っているかを確認し、次に生成された設定を見てください。自動生成ファイルと画面の両方を行き来しながら編集すると、設定の出所が分からなくなるおそれがあります。
トランスポートやプロトコル機能への対応はコアによって異なります。v2rayNGとv2flyNGはそれぞれ異なるコアのエコシステムを対象としているため、一方のクライアントが認識したリンクのパラメーターすべてに、もう一方も対応するとは限りません。デスクトップ版v2rayNでも、選択したコアとノードの機能を合わせる必要があります。未対応のトランスポート、認識されない項目、ハンドシェイク直後の切断が起きたら、ネットワークの問題を調べる前に対応状況を確認してください。複数の項目を次々に変更して試すのは避けましょう。
可能であれば、通信方式の組み合わせを一つずつ検証してください。リモート設定は固定し、多重化とポリシーは既定値のままにして、まず基本のハンドシェイクが成功するか確認します。その後、高度な設定を一項目ずつ戻してください。エラーが設定の読み込み、TCP接続、TLS、プロキシデータの送受信のどの段階で起きたか記録します。記録を残すと、確認すべき設定箇所を絞り込みやすくなります。エラーの種類から探すときはヘルプセンターもご利用ください。インストールを最初からやり直す必要はありません。
routing:ルールの順序と照合範囲
ルールに照合してからoutboundを選ぶ
routingは、すでにコアに入った接続をどの出口に送るか決めます。プロキシを使っていないアプリをコアへ取り込む機能でも、OSのプロキシ切り替えでもありません。ルールは通常、配列の先頭から順に照合され、最初に一致したルールで接続先が決まります。そのため、範囲の明確な例外を前に、広い範囲を対象とする既定ルールを後ろに置きます。TCPとUDPのすべてを対象にするルールが先にあると、それ以降の関連ルールは通常適用されません。
同じルール内の異なる項目は、通常すべての条件を満たした場合に一致します。一方、同じ項目の配列にある複数の候補値は、通常いずれか一つに一致すれば条件を満たします。たとえばinboundのタグとドメインを指定すると、指定した入口から指定したドメインへ接続するときだけ一致させる意図になります。同じ出口に送るIP範囲とドメインのグループをそれぞれ指定したい場合は、ルールを二つに分けるほうが明確です。複数の条件を一つのルールにまとめたとき、どれか一つを満たせばよいと誤解しないようにしてください。
{
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": [
"full:example.org"
],
"outboundTag": "block"
},
{
"type": "field",
"ip": [
"127.0.0.0/8",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"::1/128",
"fc00::/7",
"fe80::/10"
],
"outboundTag": "direct"
}
]
}
}
この例は、前述のdirectとblockのoutboundタグと組み合わせます。完全一致したドメインは拒否し、列挙したローカルアドレス範囲には直接接続し、それ以外は既定のoutboundに送ります。これはルールの仕組みを示す例で、すべてのアクセスを分類する設定ではありません。特にAsIsでは、IPルールとの照合だけを目的にドメインを自動で名前解決しません。プライベートアドレス範囲を追加したからといって、最終的にLAN内のアドレスへ解決されるドメインをすべて対象にできるわけではありません。
ドメインの指定方法と名前解決の方針
full:はドメイン名の完全一致、domain:は指定したドメインとそのサブドメインへの一致に使われます。regexp:では正規表現を使います。通常の文字列による一致は、想定より広い範囲に一致することがあり、設定内容も読み取りにくくなります。できるだけ明確な接頭辞を使いましょう。正規表現をJSON文字列に記述する場合は、JSONのエスケープも必要です。たとえば正規表現内のバックスラッシュを正しくエスケープしてください。ドメインの一覧に見えても、各項目の照合方法が同じとは限りません。
ルーティングのdomainStrategyでよく使われる値はAsIs、IPIfNonMatch、IPOnDemandです。前者は、ルール照合のためにドメインを積極的に名前解決しません。後者の二つは、それぞれ定められた条件で名前解決結果を使い、IPルールとの照合を試みます。早い段階で名前解決するかどうかは、クエリ回数、ルールへの一致、障害の経路に影響しますが、単純に速さや正確さが向上するわけではありません。IPによる分類がルールに必要か、DNSで利用可能な経路があるかを踏まえて選んでください。
| 項目 | 照合対象 | 主な注意点 |
|---|---|---|
domain | 接続先のドメイン | 接続先がIPだけの場合は識別が必要になることがあります |
ip | 接続先アドレスまたは名前解決結果 | 名前解決の方針に左右される |
inboundTag | 接続が入ってきた入口 | 実在するinboundタグと一致させる |
port | 接続先ポート | ローカルの待ち受けポートではありません |
ルール用リソースとモード切り替え
geosite:やgeoip:で分類する場合、対応するリソースの有無、内容が想定どおりか、クライアントがどのように更新するかも確認してください。分類名は、コアがあらゆるWebサイトを自動で認識する機能ではなく、リソースデータを参照するための名前です。読み込み時にリソースが見つからない場合は、リソースとルールの対応関係を確認してください。読み込みに成功しても結果が想定と異なる場合は、接続先がどの分類に一致したかを調べます。外部ルールで同じ名前が使われていても、リソースパッケージの内容が同じとは限りません。
クライアントの「全般」「バイパス」などのモードは、通常ルーティングのセットを生成または切り替えます。一方、システムプロキシは、どのアプリのリクエストをローカルの入口へ送るかを決めます。モードを切り替えた後、手動で追加したルールが保持されるか、どの位置に挿入されるかは、実際の設定で確認してください。二つの設定層の違いは、システムプロキシとルーティングモードの違いで解説しています。タスクトレイのアイコンだけを見て、すべてのアプリが同じ経路を使っていると判断しないでください。
ルールを検証するときは、違いを明確に確認できる接続先を一つ選び、関連する例外と既定ルールだけを残してから、ログのinboundタグとoutboundタグを確認してください。想定外のルールに一致した場合は、重複する項目を増やす前に、先にある広範囲のルール、アプリによる事前の名前解決、接続先の識別機能が使われたかを調べます。最小構成のルールで動作することを確認した後、分類ルールをグループごとに戻すと、どのグループが照合範囲を広げたか特定できます。
dns:名前解決の経路とアドレスファミリー
アプリ、システム、コアの名前解決を区別する
DNSの問題では、まずどの層でクエリが行われたかを確認してください。アプリ自身が名前解決する場合、OSがアプリの代わりに名前解決する場合、コアがルーティングや接続時に名前解決する場合があります。これらの経路は同時に存在することもあります。アプリが先にドメインをIPアドレスへ変換してからSOCKSに渡すと、コアからは元の接続先がIPアドレスだけに見えることがあります。ドメイン名を直接SOCKSに渡す場合は、コア側でもドメイン名が保持されます。どちらの経路かによって、ドメインルーティングに追加の識別が必要か、どのDNS設定が実際に使われるかが変わります。
トップレベルのdnsを設定しても、ローカル端末のすべてのドメインクエリを処理するとは限りません。主にコア自身が必要とする名前解決を設定するものです。アプリのDNSリクエストも処理するかどうかは、通信の取り込み方法やDNS転送の設計によって異なります。ブラウザー独自の暗号化名前解決が別の経路を使うこともあります。トラブル対処では、アプリ自身が名前解決しているか、どのプロキシプロトコルを使っているか、ほかのDNS機能が有効かを記録してください。OSのネットワーク設定だけから、すべてのクエリの送信元を推測しないようにしましょう。
{
"dns": {
"servers": [
"localhost"
],
"queryStrategy": "UseIP"
}
}
この部分設定では、コアがシステムの名前解決経路を使い、二種類のアドレスを問い合わせます。設定の適用範囲を確認するための基準として使えますが、あらゆるネットワークでシステムの名前解決から接続先へ到達できることを示すものではありません。ここでのlocalhostはコアが定めるシステム名前解決の方法を指し、ローカルでループバックアドレスを待ち受けるDNSサービスが必ず動いているという意味ではありません。DNSサーバーを個別に指定する場合は、現在のコアが対応する形式を使い、クエリの送信経路も確認してください。
名前解決の方針とルーティングの方針を分けて考える
queryStrategyは、コアのDNSクエリで使うアドレスファミリーを制御します。よく使われる値はUseIP、UseIPv4、UseIPv6です。IPv4に限定すると、利用できないIPv6経路を避けられる場合がありますが、IPv6アドレスしか持たない接続先には使えるアドレスがなくなります。IPv6を利用できるかは、ローカルインターフェース、デフォルトルート、出口、接続先に左右されます。システムにIPv6アドレスが表示されているだけで、経路全体が利用可能とは判断できません。
ルーティングのdomainStrategyはルール照合のために名前解決するタイミングを決め、DNSのクエリ方針は名前解決時にどの種類のアドレスを問い合わせるかを決めます。direct outbound側の方針は、接続時にアドレスをどう使うかに影響する場合があります。この三つは異なる段階の設定です。一つを変更してアクセスが復旧しても、変化した経路で通信できるようになったことしか分かりません。残りの二つも正しいとは限らないため、正常に動作する設定を保ちながら個別に確認してください。名前の似た設定をすべて同じ値に変更するのは避けましょう。
ドメインごとにDNSサーバーを選ぶ場合は、サーバーオブジェクトのドメイン範囲、期待するアドレスのフィルター、フォールバックの動作も確認してください。サーバーから回答が返ってきても、その回答が必ず採用されるとは限りません。期待するアドレスの条件を満たさない場合、コアが別の経路を試すことがあります。複雑な振り分けは、まず到達可能なDNSソースを一つ設定してから、分類やフォールバックを段階的に追加しましょう。そうしないと、サーバーに到達できない、条件で回答が除外された、フォールバックが制限されたなど、複数の原因を切り分けにくくなります。
ブートストラップと名前解決の循環
リモートサービスのアドレスがドメイン名の場合、接続を確立する前にIPアドレスを取得する必要があります。暗号化DNSサービスのアドレスがドメイン名の場合も、同様に事前の名前解決が必要です。そのクエリ経路が、まだ確立していないプロキシ出口に依存すると、循環依存が起きることがあります。起動後にタイムアウトが続き、リモートとのハンドシェイクまで進まない場合があります。設計時には、リモートアドレス、DNSサーバーのアドレス、それぞれの出口を分けて整理し、自身の結果に依存しない到達経路を少なくとも一つ確保してください。
DNSクエリがルーティングを通るか、ローカルのリゾルバーに渡されるか、暗号化クエリにプロキシを使うかは、サーバーの形式やコアの動作によって異なります。トップレベルオブジェクトの位置だけでは判断できません。特定のローカル処理を行うサーバー形式の場合、通常のルーティングタグがそのまま適用されるとも限りません。現在のコアにおけるサーバーの種類と仕様を確認し、ログと実際のリクエスト経路で検証してください。「DNSアドレスを設定した」ことを「すべてのクエリが指定した出口を通る」ことと取り違えるのは、よくある誤解です。
特定のドメインに接続できない場合は、まずDNSの回答があったか、どのアドレスファミリーの回答だったかを記録し、次に接続先のIPアドレスへ到達できるか確認してください。名前解決できても接続できるとは限らず、IPアドレスへ接続できてもTLSのサービス名が正しいとは限りません。一つのアプリだけで問題が起きる場合は、まずそのアプリ独自の名前解決設定を比較します。すべてのアプリで同じように失敗する場合は、共通のDNS経路と出口を確認してください。現象別の対処方法はヘルプセンターをご覧ください。DNSのトラブル対処に証明書検証の無効化を使わないでください。
policy:セッションポリシーと統計の設定
レベル別ポリシーはルールの優先順位ではない
policyは、セッション単位のリソースやタイムアウトの動作を管理します。levelsではユーザーレベルごとにポリシーを設定し、systemではシステム全体の統計などを管理します。レベルはルールの優先順位でも、接続速度の設定でもありません。関連するプロトコルのユーザーや接続が実際にそのレベルを使う場合にのみ、レベル別ポリシーが意味を持ちます。クライアントが設定を生成するときに既定のレベルを使う場合もあります。レベルのオブジェクトを手動で追加しても、すべての接続が自動的に切り替わるわけではありません。
JSONではレベルのキーを"0"のような文字列で記述し、その内部の項目は仕様に従って数値または真偽値を使います。ポリシーではoutboundを作成できず、証明書のエラーも修正できません。ルーティングで選ばれていないノードを自動的に接続へ参加させる機能もありません。値を調整する前に、問題がハンドシェイクのタイムアウト、アイドルセッションの切断、統計の欠落のどれに当たるか確認してください。JSONの読み込みや待ち受けポートの段階で起きたエラーには、ポリシーはまだ関与していません。これらの値を変えても原因には影響しません。
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": false,
"statsUserDownlink": false
}
},
"system": {
"statsInboundUplink": false,
"statsInboundDownlink": false,
"statsOutboundUplink": false,
"statsOutboundDownlink": false
}
}
}
上記は、項目間の関係を示すための明示的なポリシー設定例で、すべてのネットワークに推奨する固定の組み合わせではありません。時間の項目は秒単位ですが、実際に適用される状況はコアの実装によって異なります。現在の設定に明確な問題がなければ、クライアントが生成する既定のポリシーを保つほうが、いわゆる「最適化設定」をそのまま流用するより管理しやすいでしょう。調整する場合は、再現可能な問題と確認したい結果を決めてください。一度だけページが読み込めた感覚で、ポリシーの良し悪しを判断しないでください。
ハンドシェイク、アイドル状態、単方向接続
handshakeは、関連するハンドシェイク段階で使える時間を制限しますが、すべての下位層のタイムアウトを一律に制御するものではありません。ドメインの名前解決、トランスポート接続、アプリ独自の待機時間は、別の仕組みで管理される場合があります。短すぎる設定は、応答の遅い接続を途中で切断する原因になります。長すぎると、利用できない接続がより長くリソースを占有することがあります。トラブル対処では、接続ライフサイクルのどの段階でエラーが起きたかを確認してから、このポリシー項目が関係するか判断してください。
connIdleは、データのやり取りがないアイドル状態の接続を対象とします。継続的に通信するダウンロード接続と、長時間データが流れないメッセージ用チャネルは、同じ状態ではありません。アプリによっては、ハートビートで接続を維持します。その間隔と、コアによるアイドル接続の切断、中間機器のセッションタイムアウトが互いに影響することがあります。アイドル時間を無制限に延ばしても、ルーターや接続先による切断を防げるわけではなく、失われたネットワーク経路が直るわけでもありません。
uplinkOnlyとdownlinkOnlyは、セッションの通信が片方向だけになったときの待機動作を制御します。半閉鎖状態の接続は、一部のプロトコルでは正常な動作であり、すぐに失敗と判断できるものではありません。アップロード、ストリーミング応答、ロングポーリングで特別な要件があるアプリでは、まず既定のポリシーで確認してから、ログと接続の動作を見て個別に比較してください。待機時間をむやみに短くすると、応答が届く前にセッションが終了することがあります。
| 項目 | 制御する段階 | 代わりにはならない確認項目 |
|---|---|---|
handshake | 関連するセッションのハンドシェイク | 認証情報とセキュリティ設定 |
connIdle | 接続のアイドル期間 | 中間機器とアプリのハートビート |
uplinkOnly | 単方向の通信状態 | アプリの半閉鎖動作 |
statsOutboundUplink | outboundのアップロード統計 | 統計APIとクライアントの表示 |
統計機能の依存条件とリソースの範囲
統計を有効にしても、クライアント画面に数値が表示されるとは限りません。コア側で統計モジュールを有効にし、読み取り用APIを提供し、クライアントがそのAPIを照会する必要がある場合もあります。ユーザー単位の統計では、プロトコルのユーザー識別情報などが必要になることがあります。匿名のローカルinboundから、区別可能なユーザー情報が自動的に生成されるわけではありません。ローカル接続が特定のoutboundに入ったかを確認するだけなら、統計APIを用意するよりアクセスログを見るほうが簡単な場合があります。トラブル対処のために管理用の待ち受けを追加公開しないでください。
接続統計、ネットワークインターフェースの通信量、サービスの課金では、集計方法が異なる場合があります。トランスポートのオーバーヘッド、接続の多重化、別プロセスの通信、集計のタイミングなどによって差が生じるため、複数の画面で数値が常に一致するとは限りません。設定上の統計は、対象オブジェクトの通信量の変化を見るためのものです。サービス提供元の最終的な課金額を示すとは限りません。ノードの通信量倍率についてはサービスの説明を確認し、ポリシー項目の変更で課金額を変えようとしないでください。
ポリシーの調整では、端末のリソースやOSの動作も考慮してください。デスクトップとAndroidでは、バックグラウンド動作の制限、スリープ、ネットワークの切り替え方が異なります。長時間接続の切断は、コアのアイドルポリシーではなくOSによる終了が原因のこともあります。v2rayNGまたはv2flyNGがバックグラウンドで切断される場合は、まずOSのバッテリー管理とネットワーク権限を確認してください。v2rayNでPCのスリープ復帰後に問題が起きる場合は、接続を再作成した結果と比較します。コアがポリシーに従ってセッションを切断したと確認できた場合に限り、該当するタイムアウトの調整を検討してください。
設定の検証、ログ、段階的なトラブル対処
まず読み込みを検証し、次にアクセスを確認する
検証は、設定構文から始めて、コアの構造、待ち受け、接続の取り込み、ルーティング、名前解決、トランスポート、アプリの結果へと進めます。構文が正しいというのは、パーサーがJSONを読み込めるという意味にすぎません。プロトコル項目がコアに対応していることを示すものではありません。コアが正常に起動しても、基本構造やリソースを読み込めたというだけで、リモート接続が使えるとは限りません。各段階で成功を示す記録を分けて残すと、「ファイルを開けるから設定に問題はない」「ブラウザーで失敗したからノードが停止している」といった、異なる段階を結びつけた誤った判断を避けられます。
クライアントで、正常に動作する設定を保存するかコピーをエクスポートしてから、個別のテスト設定を作成してください。クライアントに設定テストやログの機能がある場合は、実際に選択中のコアがテスト対象か確認してから利用します。カスタム設定を使って実行する場合は、クライアントがルーティングを追加するか、ポートを書き換えるか、別のオプションを統合するかも確認してください。テストファイルと実行ファイルが異なる場合、構文を完全に検査しても実際の動作は説明できません。検証のたびに、どのファイルが読み込まれているか確認しましょう。
以下のコマンドは、Pythonの標準ライブラリでJSONの構文を確認し、整形するだけのものです。V2FlyやXrayの項目の意味を検証したり、接続を開始したりはしません。コマンド中のconfig.jsonは、現在のディレクトリにあるローカルのテスト用ファイルを指します。WindowsでPythonランチャーを使う場合は、2つ目のコマンドを利用できます。必要なツールがインストールされていない場合は、クライアントのテスト機能を使ってください。リファレンスを読むために実行環境を変更する必要はありません。
python3 -m json.tool config.json
py -m json.tool config.json
一時的にログの詳細度を上げる
loglevelはログの詳細度を制御します。通常は必要最小限にし、トラブル対処の間だけ一時的に詳細度を上げるとよいでしょう。以下の例では情報レベルのエラーログを有効にしていますが、出力先のファイルは指定していません。ログの出力先は、実際の起動方法によって決まります。アクセスログが出力されるか、どこに出力されるか、クライアント画面に表示できるかは、それぞれ確認が必要です。エラーログの詳細度を上げれば完全なアクセス記録も得られると考えないでください。
{
"log": {
"loglevel": "info"
}
}
ログを確認するときは、最後の「接続失敗」という一文だけでなく、最初に現れたエラーを探してください。未知のプロトコルはコアの対応状況やプロトコル名、アドレス使用中のエラーはinboundの待ち受け、リソースが見つからないエラーはルール用データ、名前解決のタイムアウトはDNSの経路、証明書名のエラーはセキュリティ層の識別に関わる可能性があります。同じ操作を繰り返すと似た記録が増えるため、テスト開始時刻を記録し、対象を明確にしたリクエストを一つだけ実行してください。他のアプリの通信を減らすと、原因を判断しやすくなります。
ログには接続先のドメイン、アドレス、サブスクリプションURLの一部など、利用状況が分かる情報が含まれることがあります。共有前に、認証情報、完全なサブスクリプションURL、無関係なアクセス記録を削除し、必要なエラーの種類や項目だけを残してください。ユーザー情報を含む設定全体を公開の質問サイトに貼らないでください。トラブル対処後は通常のログレベルに戻し、ローカルログの保存先とローテーション方法を確認しましょう。後から分析する価値のない記録を長期間ためないようにしてください。
最小構成で再現し、元に戻す
inboundは正常なのにアクセスに失敗する場合は、まずこのページのdirect設定例で、アプリからローカル入口まで通信できるか確認します。次に、必要な情報が揃ったリモートoutboundを一つだけ戻してください。その後、ルーティングを一つ追加し、最後にDNS分類とポリシーを戻します。各段階で同じテスト用アプリと接続先を使い、成功または失敗の結果とログを記録してください。ノード、リソース、上書きルールが多数ある設定で原因を同時に探すのではなく、どの変更で問題が起きたかを特定しやすくなります。
サブスクリプション更新の失敗は、「サブスクリプションの取得」と「取得したノードを使った接続」に分けて確認してください。前者はリンクの状態、クライアントの更新経路、証明書の問題などの影響を受けます。後者はノードの設定項目とコアに依存します。ノードに接続できてもサブスクリプションURLが有効だとは限らず、サブスクリプションを更新できても、すべてのノードに接続できるとは限りません。状況に応じた手順はサブスクリプション更新に失敗したときの対処法をご覧ください。更新エラーが起きたからといって、まずrouting全体を書き換えないでください。
別のOSへ移行する場合、設定の論理的な構成は引き継げることが多い一方、ファイルパス、待ち受けポートの競合、システムプロキシ、バックグラウンド動作の制限は改めて確認する必要があります。WindowsとmacOSの初回起動で問題が起きた場合は、初回インストールと初期設定を参考にしてください。Linuxではデスクトップ環境のプロキシ設定とアプリ独自の環境設定を区別し、Androidではシステムの通信取り込みとバックグラウンド管理に注意してください。移行できたかどうかは、ファイルのコピーがエラーなく終わったかではなく、アプリの通信が想定どおりコアに入り、コアから出ていくかで判断します。
最後に、動作する設定の保存、通常のログレベルへの復帰、タグ参照の確認、待ち受け範囲の確認、再起動後も変更が保たれるかの検証を行います。その後、Web閲覧、ダウンロード、長時間接続を使う実際のアプリをテストしてください。単一のWebページだけで、すべての利用状況を代表させないようにしましょう。項目の意味が分からない場合は該当する章に戻り、操作手順を最初から確認する場合はクイックスタートガイドをご覧ください。このリファレンスは設定項目の関係を説明し、ガイドは操作を順に進めるためのものです。互いの代わりにはなりません。