JSON構造の概要と設定の読み込み順序
V2RayとXrayのメイン設定は、トラフィック処理パイプラインを表すJSONオブジェクトとして捉えられます。inboundsが接続を受け付け、ルーティングが行き先を判断し、outboundsが接続を実行します。DNSとポリシーのオブジェクトは、このパイプラインに名前解決やリソース制御を加えます。
トップレベルオブジェクトは実行手順の一覧ではない
設定ファイルの最外層は1つのJSONオブジェクトで構成され、一般的なキーにはlog、dns、inbounds、outbounds、routing、policy、statsがあります。これらのキーは、通常、本文中の記述順で実行順が決まるわけではありません。JSONオブジェクトはキー名で構造を表し、記述位置で処理の流れを表すものではないためです。順序が意味を持つ主な場所は配列です。たとえばrouting.rulesは先頭から順に照合され、最初に一致したルールがoutboundを決めます。複数のinboundsとoutboundsも、それぞれのtagを通じて他のオブジェクトから参照されます。
設定を読むときは、まずすべてのタグを記録し、その後に参照関係をたどって確認する方法が安全です。あるinboundのタグはルーティングルールのinboundTagで使われ、outboundのタグはoutboundTagから指定され、DNSサーバーもtagを介してルーティングルールと結び付く場合があります。タグは設定内部の識別子にすぎず、自動的にトラフィック分岐を発生させるものではありません。存在しないタグをルールに記述すると、起動時にエラーになる場合もあれば、該当経路の実行時にトラフィックの行き先がなくなる場合もあります。具体的な挙動はコアとフィールドの位置によって異なります。
{
"log": {
"loglevel": "warning"
},
"dns": {
"servers": ["1.1.1.1", "localhost"]
},
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"udp": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "blocked",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
}
]
}
}
配列・オブジェクト・値の型
inbounds、outbounds、routing.rulesはいずれも配列なので角括弧を使います。単一のinbound、outbound、ルールはオブジェクトであり、波括弧を使います。ポートは数値で指定し、単位付きの文字列にはしません。真偽値はtrueまたはfalseだけです。フィールドの省略とnullは同じ意味ではありません。省略するとコアの既定動作が適用されますが、明示的な空値はフィールド定義が許可する場合にのみ意味を持ちます。起動失敗の多くはプロトコルの問題ではなく、括弧の閉じ忘れ、オブジェクト間のカンマ抜け、文字列での全角引用符の使用、配列であるべきフィールドを単一文字列にすることが原因です。
標準JSONではコメントを使用できず、最後のメンバーの後に末尾カンマを置くこともできません。Web上の例では説明のために//コメントを使うことがありますが、クライアントのコア設定へコピーする前に削除してください。v2rayN、v2rayNG、v2flyNGでは、グラフィカルな画面とは別に独自の設定層を管理している場合があります。画面上のノード、サブスクリプション、ルーティング設定は変換された後にコアへ渡されます。そのため手動編集の前に、編集対象がクライアント管理ファイル、生成済みの実行設定、独立したコアが読み込む設定のどれなのかを確認してください。ファイル名が似ているからといって、同じオブジェクトと判断してはいけません。
最小構成から段階的に複雑化する
設定を確認するときは、ローカルのinboundを1つ、明確に動作するoutboundを1つ、最小限のルーティングだけを残し、そこへDNS、スニッフィング、複数のトランスポート、統計ポリシーを段階的に追加してください。一度に複数のモジュールを追加すると、エラーの原因が互いに隠れてしまいます。たとえば接続失敗は、リモート側のパラメータ、誤ったDNS経路によるドメイン解決、または先行ルールが接続をblockedへ送ったことが原因かもしれません。最小化とは必要なセキュリティフィールドを削除することではなく、変数の連鎖を短くすることです。サーバー側が要求するアドレス、ポート、ユーザー識別子、トランスポート、セキュリティパラメータは維持し、通信に影響しない追加ルールだけを一時的に外します。
保存前に、文字コードがUTF-8であること、ファイルが1つのルートオブジェクトだけで構成されていること、キー名の大文字・小文字がフィールド定義と一致していることも確認してください。outboundTagとoutboundtagは別の名前です。プロトコル名も画面上の訳語から推測してはいけません。構造を確認したら、inbounds、outbounds、ルーティングの3部分を順に検証します。大きな設定でポートを何度も変更するより、結果を説明しやすくなります。
inbounds、リスニング範囲、プロトコル設定
inboundは、コアがどこで接続を受け付けるか、どのプロトコルで接続を解釈するか、宛先アドレスをスニッフィングするかを定義します。デスクトップクライアントではローカルのSOCKSやHTTP inboundを作成することが多く、モバイルではVPNサービスがトラフィックをコアへ渡す場合があります。
listen・portとローカル公開範囲
各inboundでは、少なくともプロトコルとその設定を明確にする必要があります。一般的なフィールドにはtag、listen、port、protocol、settings、sniffing、streamSettingsがあります。listenを127.0.0.1にすると、そのポートへ接続できるのは同じ端末上のプログラムだけです。ブラウザー、システムプロキシ、ローカルツールの入口として適しています。すべてのネットワークインターフェースで待ち受けると、同じLAN上の他の端末からもアクセスを試みられる可能性があるため、OSのファイアウォール、認証、実際の共有要件を同時に検討してください。LAN共有の明確な目的がない場合、ローカルループバックアドレスの方が境界を明確にできます。
portは他のプログラムに使用されていない必要があります。同じアドレスとポートの組み合わせを、同時に動作する2つのinboundで重複して待ち受けることはできません。GUIクライアントはSOCKS、HTTP、混合入口に隣接したポートを割り当てることがありますが、これらはプロトコル固有の固定値ではありません。チュートリアルのポートを別の端末へそのまま移すと、開発サーバー、古いクライアントプロセス、他のネットワークツールと競合する可能性があります。「アドレスはすでに使用されています」といったエラーが出たら、まず重複したプロセスを終了するか、クライアント設定でリスニングポートを変更し、システムプロキシの向き先も更新してください。
{
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
},
{
"tag": "http-in",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
]
}
SOCKS・HTTP・透過型入口の違い
SOCKS inboundは、アプリが明示的に開始したプロキシ接続を処理できます。udpを有効にすれば、SOCKSのUDP転送手順に対応したリクエストも受け取れますが、端末上の任意のUDP通信が自動的にこのポートへ入るわけではありません。HTTP inboundは、HTTPプロキシ設定に対応したアプリを主に処理します。システムプロキシは、通常、システム設定を読み取るソフトウェアにだけ影響し、端末上のすべての通信を取得するものではありません。透過プロキシや仮想ネットワークインターフェースによる接続には、OSのネットワークルールとクライアントの連携が必要で、フィールドや権限の条件も複雑です。通常のSOCKS inboundのプロトコル名だけを置き換えて使うことはできません。
v2rayNでは、画面からローカル入口を作成し、Windows、macOS、Linuxのシステムプロキシ状態を設定します。v2rayNGとv2flyNGでは、AndroidのVPNサービスが選択したアプリのトラフィックをコアへ渡します。つまり、同じノードのoutboundパラメータはクライアント間で移行できますが、inbound部分はプラットフォームとの統合方式に依存することが多いということです。移行時はサーバーパラメータを保持し、移行先のクライアントにローカル入口を再生成させてください。実行設定全体を上書きするのは避けます。プラットフォーム別のインストールとクライアントの位置付けはクライアントのダウンロードページで確認できます。
sniffingの役割と誤判定の限界
スニッフィングは、接続初期のデータからドメイン名を復元し、本来は宛先IPしか持たないリクエストにもドメインルーティングを適用できるようにします。destOverrideでよく使われるhttpとtlsは、それぞれ識別可能なHTTPホスト情報とTLSハンドシェイクのドメイン名に対応します。スニッフィングはアプリの内容を復号するものではなく、すべての接続でドメイン名を復元できる保証もありません。暗号化ハンドシェイクの変化、非標準プロトコル、接続の多重化、IPアドレスを直接使うアプリでは、利用可能なドメイン名を取得できない場合があります。
スニッフィングを有効にした後、特定のアプリで宛先アドレスが書き換えられて接続に異常が出た場合は、そのアプリのトラフィックを専用inboundへ分けるか、上書きする種類を制限し、有効時と無効時のログを比較してください。スニッフィングをDNSの代替と考えてはいけません。DNSはドメイン名の解決方法を決めるのに対し、スニッフィングは接続が入った後に元のドメイン名を識別しようとする機能であり、処理段階が異なります。ルーティングルールにドメイン条件とIP条件の両方がある場合は、domainStrategyも確認し、ルーティング対象の追加解決が実行されるか判断してください。
| フィールド | 一般的な値 | 確認ポイント |
|---|---|---|
listen |
127.0.0.1 |
本当に他の端末からのアクセスを許可する必要があるか |
port |
有効なポート番号 | 他のプログラムやinboundと重複していないか |
protocol |
socks、http |
対応するプロトコルのsettingsか |
tag |
重複しない任意の名前 | ルーティングでの参照が完全に一致しているか |
outbounds、ノードパラメータ、チェーン構成
outboundは、ルーティングで選ばれた接続を目的地へ送ります。リモートのプロトコルサーバーへ接続することも、目的地へ直接アクセスすることも、接続を拒否することも、別のoutboundへ渡して処理を続けることもできます。
プロトコルフィールドは一体として確認する
リモートoutboundは通常、protocol、settings、streamSettingsで定義されます。VLESSを例にすると、サーバーアドレスとポートはvnext項目、ユーザー識別子はusersに入り、トランスポート方式、セキュリティ層、サーバー名はstreamSettingsに入ります。アドレス、ポート、ユーザー識別子だけを確認しても設定の一致は証明できません。クライアントとサーバーのトランスポート、TLS系セキュリティ設定、パス、ホスト名、関連する拡張パラメータも相互に対応している必要があります。
設定画面では、これらのフィールドが「アドレス」「ユーザー」「トランスポート」「セキュリティ」などのページに分かれていることがあります。一方、JSONでは隣接するオブジェクトにまとめられます。サブスクリプションのインポートに異常がある場合は、まずクライアントの詳細画面で各項目を確認し、ノードの表示名だけで判断しないでください。ノード名は通常、ローカルのメモにすぎず、プロトコルのハンドシェイクには関与しません。接続に影響するのは構造化されたフィールドです。手動でパラメータを移す場合、URIでエンコードされた文字列をそのままJSONの値として扱わないでください。また、画面で「デフォルト」を示す空欄を、勝手に"default"という文字列へ変換しないでください。
{
"outbounds": [
{
"tag": "remote-vless",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 443,
"users": [
{
"id": "11111111-1111-4111-8111-111111111111",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "server.example.com"
},
"wsSettings": {
"path": "/example-path",
"headers": {
"Host": "server.example.com"
}
}
}
},
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "blocked",
"protocol": "blackhole"
}
]
}
directとblockedは明確な処理経路
freedom outboundは通常、目的地へ直接接続するために使われ、directというタグを付けることが多いです。blackholeはルールで選ばれた接続を終了するために使われ、blockedと名付けるのが一般的です。これらのタグは慣例的な名称にすぎないため変更できますが、ルーティング側の参照も同時に更新する必要があります。freedomをoutbounds配列に置くだけでローカルアドレスが自動的に直結されるわけではなく、該当するトラフィックをルールで指定する必要があります。同様に、blackholeを定義しただけではどのドメインも自動的に遮断されません。
どのルールにも一致しない場合、コアは通常、デフォルトoutboundを選択しますが、具体的な挙動は設定構造とコアの実装によって異なります。設定を読む人が暗黙の順序に依存しないよう、大規模な設定では重要なトラフィックに明示的なルールを記述し、用途が分かるoutboundタグを使うのが適切です。リモート、直接接続、ブロック用outboundの名称には、混同しやすい連番ではなく用途を反映させてください。名称が明確なら、ログのoutbound識別子も設定と対応付けやすくなります。
outboundチェーン・プロキシ設定・ループ
一部のコアでは、proxySettingsなどの仕組みにより、あるoutboundが別のoutboundを経由して接続を確立できます。明確に設計された多段出口には有効ですが、DNS、ハンドシェイク、障害切り分けの層が増えます。チェーンの各段には一意のタグを付け、AがBを指し、BがAを指すような循環を作らないでください。リモートサーバーのドメイン名をどう解決するかも別途検討が必要です。最初の接続を確立するためのDNS問い合わせが、まだ確立していないoutboundに依存すると、起動後もタイムアウトが続く循環が発生します。
チェーン構成を確認するときは、まず物理接続を最後に担当する単一のoutboundを単独で動作させ、そこから前段へ一層ずつ追加してください。ログに「接続が閉じられました」としか出ない場合は、outboundタグを手がかりに、どの層で失敗したかを判断します。リモート側の拒否、ドメイン解決の失敗、TLSサーバー名の不一致、ローカルルーティングによる誤ったoutbound選択は、いずれもアプリでページを開けないという同じ症状に見えることがあります。すべてのプロトコルパラメータを同時に置き換えるより、層ごとの検証の方が証拠を残せます。
デスクトップではv2rayNでノードとコア設定を管理するのが基本です。Androidでは、使用するコアに応じてv2rayNGまたはv2flyNGを選択できます。XrayとV2Flyの位置付けを比較したい場合は、XrayとV2Flyのコアの違いも参照してください。コアの系統によって対応フィールドの範囲が異なる場合があるため、複雑な設定を移行する前に、移行先クライアントで実際に有効なコアを確認してください。
routing、照合条件、優先順位
ルーティングオブジェクトは接続を生成するものではありません。inboundが識別した宛先情報を受け取り、ルール配列に従ってoutboundを選択します。「先頭から順に確認し、最初に一致したものを採用する」ことが、分岐設定を管理するうえで重要です。
ルール配列の照合モデル
routing.rulesでよく使われるルールタイプはfieldです。domain、ip、port、network、protocol、inboundTag、userなどの条件で接続を絞り込み、outboundTagで行き先を指定します。同じルール内に異なる種類の条件が複数ある場合は、通常それらをすべて満たす必要があります。同じフィールドの配列に複数の値がある場合は、通常いずれか1つに一致すればよいことを表します。管理時はドメイン一覧だけでなく、同じルールにポートやinboundタグの制限が付いていないかも確認してください。
ルールは配列の先頭から順に確認されます。より具体的な例外は、より広いルールより前に置いてください。たとえば、リモートoutboundを通す必要がある内部テスト用ドメインは、対象範囲の広い直接接続用ドメイン集合より前に配置します。広いルールが先に一致すると、後続の例外は実行されません。最後に残りの接続をまとめて処理するルールを置くことも、デフォルトoutboundに任せることもできますが、前者の方が確認しやすい場合が多いです。ルールを調整するときは、一度に1つのグループだけを移動し、移動前後の一致タグを記録してください。「ルールに一致しない」ことと「outboundが使えない」ことを混同しないためです。
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"domainMatcher": "hybrid",
"rules": [
{
"type": "field",
"domain": [
"full:intranet.example",
"domain:local.example"
],
"outboundTag": "direct"
},
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "direct"
},
{
"type": "field",
"protocol": [
"bittorrent"
],
"outboundTag": "blocked"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "remote-vless"
}
]
}
}
domain構文とIP条件
full:は完全なドメイン名との一致を表し、特定のホスト名だけを処理する場合に適しています。domain:は通常、指定ドメインとそのサブドメイン範囲に一致します。regexp:では正規表現を使えますが、複雑な式は保守コストを増やし、ドット、境界、エスケープを誤りやすくなります。プレフィックスのない記法の解釈は、使用するコアの定義に従ってください。geosite:やgeoip:などのルールセット識別子は、コアが読み込めるルールデータに依存します。名前が存在していても、ローカルデータに対応する項目が含まれているとは限りません。
IPルールには単一アドレスもCIDRネットワークも指定できます。geoip:privateはプライベートアドレス範囲を対象にするためによく使われ、LAN内の端末やローカルサービスをリモートoutboundへ送らない構成に役立ちます。ただし、ドメインが最終的にプライベートアドレスへ解決された場合にIPルールへ進むかどうかは、domainStrategyにも左右されます。AsIsでは、ルーティング段階でIPルールに一致させるための積極的なドメイン解決は行われません。IPIfNonMatchでは、ドメインルールに一致しなかった後で解決し、IP照合を続ける場合があります。より積極的な戦略では、さらに早く解決されることがあります。戦略が積極的になるほど、ルーティングとDNSの結び付きも強くなります。
inbound・ネットワーク・プロトコルによる分岐
inboundTagは、異なるローカル入口を異なるoutboundへ送る場合に適しています。たとえばテスト用入口だけを特定のリモートoutboundへ固定し、通常のシステムプロキシはドメインルールで処理できます。実験用の経路を分離する用途にも便利です。専用ポートとタグを追加すれば、メイン入口を変更せずにルールを検証できます。networkではTCPとUDPを区別できますが、リモートプロトコルとトランスポートが実際にそのネットワーク種別を完全に運べるかは別途確認が必要です。protocol条件はコアの識別結果に依存し、inboundのスニッフィングと関係することが多いため、すべての接続を確実に分類できるとは考えないでください。
分岐結果は、クライアント画面の「グローバル」「ルール」「直接接続」などのモードにも関係します。画面上のモードは単一ルールを単純に変更するのではなく、異なるルーティングテンプレートへ切り替えたり、デフォルトoutboundを変更したりする場合があります。代表的な3つのモードの適用範囲は、システムプロキシ・グローバルモード・中国本土バイパスモードの違いを参照してください。カスタムルールをデバッグするときはクライアントのモードを固定します。画面上の切り替えで設定が再生成され、手動変更が無効になったように見えることがあるためです。
| 条件 | 表現に適した内容 | よくある誤り |
|---|---|---|
domain |
完全なドメイン名、サフィックス範囲、ルールセット | プレフィックスの意味やルール順を見落とす |
ip |
単一アドレス、CIDR、IPルールセット | ルーティング段階でドメインが解決されるか確認していない |
inboundTag |
ローカル入口ごとの経路分離 | 存在しない、または表記の異なるタグを参照する |
network |
TCPとUDPの区別 | ネットワーク種別をアプリケーションプロトコルの分類と混同する |
dns設定、サーバー選択、ルーティング連携
DNS設定は、コアがドメイン名を解決するときにどのサーバーへ問い合わせるか、特定のドメインをどの解決経路へ優先的に送るかを決めます。OSのDNS、アプリ独自の名前解決、ルーティング段階の解決とは別の層です。
まず、誰が問い合わせを開始したかを区別する
アプリはシステム層で先に名前解決を行い、宛先IPだけをプロキシへ渡すことがあります。ドメイン名をそのままSOCKSやHTTP inboundへ渡すこともあり、独自の暗号化DNSを使うアプリもあります。トップレベルのdnsオブジェクトが直接制御できるのは、コアに入り、コア自身が実行する問い合わせだけです。設定を変更しても期待した変化がない場合は、inboundが受け取ったのがドメイン名かIPかを確認し、スニッフィングでドメイン名が復元されたかも確認してください。dns.serversを変更するだけで、すべてのシステムプログラムに独自の解決経路を強制できるわけではありません。
serversには単純なアドレスだけでなく、address、domains、expectIPs、skipFallbackなどを持つオブジェクトも指定できます。単純な配列は単一経路に適し、オブジェクト形式はドメインごとにサーバーを選ぶ場合に適しています。サーバー一覧は常に「1番目が失敗したら2番目を使う」という従来型の予備サーバーとして動作するわけではありません。ドメインの絞り込み、フォールバック設定、コアの実装が問い合わせ先を決めます。複雑なDNSを設計する前に、役割の明確な2台のサーバーで検証し、その後にドメイン範囲とフォールバック制限を追加してください。
{
"dns": {
"hosts": {
"router.example": "192.168.1.1"
},
"servers": [
{
"address": "localhost",
"domains": [
"full:router.example",
"domain:internal.example"
],
"skipFallback": true
},
{
"address": "1.1.1.1",
"domains": [
"domain:public.example"
]
},
"8.8.8.8"
],
"queryStrategy": "UseIP"
}
}
hosts・domains・期待する結果
hostsは、特定の名前に静的な対応付けを提供します。少数の安定したローカルサービス名には適していますが、大規模で変動するドメイン一覧には向きません。静的マッピングは優先度が高いため、アドレス変更後に更新を忘れると、「特定の名前だけが古いアドレスを指し続ける」状態になります。確認時は、OSのhostsファイル、クライアント画面のホスト上書き項目、コア設定のdns.hostsを同時に調べ、複数の層が競合していないか確認してください。
サーバーオブジェクトのdomainsは、そのサーバーへ渡すドメインを絞り込みます。構文はルーティングのドメイン条件に近いものの、用途は異なります。前者はリゾルバーを選び、後者は接続先のoutboundを選びます。expectIPsは、返されるアドレスが想定範囲に入るかを制約するために使いますが、任意の応答を対象範囲へ書き換えるものではありません。制約が狭すぎると正常な応答も除外され、フォールバックへ進む可能性があります。skipFallbackは、そのサーバーがフォールバックに参加する方法に影響します。組み合わせる前に、「ドメイン選択—サーバー問い合わせ—結果確認—接続ルーティング」の4段階を整理してください。
DNS問い合わせでoutboundを選ぶ仕組み
DNSサーバーのアドレス自体も接続を確立する必要があります。サーバーをドメイン名で指定すると、まずリゾルバーのドメイン名をどう解決するかという問題が生じます。IPで指定すればこの層は省けますが、ネットワークのoutboundは依然としてルーティングで選ばれます。複雑な設定では、DNSトラフィックにタグを付け、ルーティングでそのタグやプロトコルに応じて特定のoutboundへ送ることがあります。この場合は循環を避けてください。リモートoutboundがドメイン解決に依存し、そのドメイン解決もまだ確立していないリモートoutbound経由を要求すると、循環が発生します。
説明可能な経路を作るには、まずリモートサーバーのアドレスに安定した基礎解決経路を用意し、その後で通常の対象ドメインをどう分岐するか決めます。domainStrategyとDNSルールを併用すると、IP条件を判定するためにルーティングが行う解決もDNSモジュールへ入ります。そのため、表面的にはルーティングの問題でも、実際にはDNSサーバーの選択やフォールバック結果が原因かもしれません。ログは最後のタイムアウトだけを切り取らず、問い合わせ、アドレス取得、outbound選択、接続先の順に時系列で確認してください。
キャッシュ・IPv4・IPv6の選択
queryStrategyは、問い合わせ結果として返すアドレスファミリーを制限または優先するために使います。対応範囲はコアによって異なります。アドレスファミリーを1種類に限定すると、ローカルネットワークの不完全な接続性を回避できる場合がありますが、本来到達可能な宛先まで除外する可能性があります。まずOSが対象のネットワークへ接続できるか確認し、そのうえで問い合わせを制限するか判断してください。クライアント、コア、システムリゾルバーのいずれも結果をキャッシュする可能性があるため、DNS変更直後の再試行で新しい問い合わせが発生するとは限りません。関連するクライアントプロセスを再起動し、接続を再確立する方法はありますが、キャッシュ削除を恒久的な設定解決策と考えないでください。
「ドメインでは接続できないが、直接IPなら接続できる」場合は、まず問い合わせがアドレスを返しているか確認し、次にそのアドレスがルーティングで想定したoutboundへ送られているかを調べます。「一部のドメインで断続的に失敗する」場合は、失敗時と成功時に選ばれたDNSサーバー、アドレスファミリー、outboundタグを比較してください。サーバーを変更するだけでは、リゾルバー経路の検証はできても、ルール条件の確認にはなりません。一般的なクライアントの接続手順と検証方法は、入門ガイドで順に確認できます。
policy、接続タイムアウト、統計スイッチ
policyは、トラフィックをどのノードへ送るかを決めるものではありません。ユーザーレベルとシステム全体の動作に対して、接続タイムアウト、アイドル時間、統計スイッチを設定します。ルーティングの外側にありますが、長時間接続のライフサイクルと可観測性に影響します。
levelとlevelsの参照関係
policy.levelsは、ユーザーレベルをキーとするオブジェクトです。プロトコルのユーザー設定にあるlevelの数値が対応するポリシーを参照します。明示的に設定されていない場合は、通常デフォルトレベルが使われます。レベルは速度評価でも権限の自動的な上下関係でもなく、一連のユーザー接続を一連のポリシーパラメータへ対応付けるためのものです。レベルを0から1へ変えても性能が自動的に向上するわけではありません。levels["1"]を定義し、異なるフィールドを設定して初めて実際の差が生じます。
クライアントがローカル接続の開始側になる場合、多くのユーザーはデフォルトレベルだけで十分です。複数レベルは、異なるinboundユーザーを区別する必要があるサーバー設定でよく使われます。それでも実際の管理要件に基づいて設計し、ユーザーごとにほぼ同じポリシーを作らないようにしてください。ポリシーが増えるほど、移行時に参照漏れが起きやすくなります。確認時はまずすべてのlevelを検索し、policy.levelsに対応するキーが存在することを確認します。JSONオブジェクトのキーが文字列形式で記述される点にも注意してください。
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": false,
"statsUserDownlink": false
}
},
"system": {
"statsInboundUplink": true,
"statsInboundDownlink": true,
"statsOutboundUplink": true,
"statsOutboundDownlink": true
}
},
"stats": {}
}
ハンドシェイク・アイドル・単方向接続のタイムアウト
handshakeは、接続初期の確立を待てる時間を制御します。短すぎると、高負荷の端末、低速ネットワーク、多層のハンドシェイクが必要な接続が完了する前に切断されます。長すぎると、失敗した接続がより長くリソースを占有します。connIdleは、通信がない接続をどれだけ保持するかを判断します。インスタントメッセージ、プッシュ通知、リモート端末、ストリーミングではアイドル状態の挙動が異なるため、Web閲覧のテストだけで長時間接続の値を一律に決めないでください。
uplinkOnlyとdownlinkOnlyは、接続が単方向の転送だけになった後のライフサイクルを処理します。アップロードやダウンロードの速度制限ではなく、どちらか一方向に帯域を割り当てるものでもありません。単方向の転送後にアプリが早期切断される場合は、これらの値を確認してください。双方向ともデータがない状態で切断されるなら、connIdleが関係している可能性が高くなります。リモートサーバー、中間ネットワーク機器、アプリ自身が接続を閉じる場合もあるため、ローカルのpolicyだけで原因を判断しないでください。
統計フィールドには一連の有効化設定が必要
statsUserUplinkとstatsUserDownlinkはユーザーレベルの統計を制御し、policy.system内のフィールドはinboundとoutbound方向の統計を制御します。真偽値をtrueにするだけで、クライアント画面にデータが表示されるとは限りません。トップレベルのstatsオブジェクト、対応するAPI、クライアントの読み取り処理も必要です。逆に、画面に表示されないからといって、コアに統計機能がないと断定することもできません。「カウンターが有効でない」「カウンターは存在するが読み取られていない」「読み取りインターフェースとクライアント表示が一致していない」の3つを区別してください。
統計機能は追加の状態管理を伴うため、診断や管理の要件に応じて有効化を判断します。統計結果を読み取らない個人用デスクトップ設定では、簡略化したままで構いません。特定のinboundやoutboundに通信があるか分析したい場合は、システムレベルのカウンターを一時的に有効にし、ログでタグを確認してください。統計値だけを接続品質の判断基準にしないでください。統計値が示すのは対応する処理を通過したデータ量の変化であり、ハンドシェイク時間、アプリの応答、DNS選択を直接説明するものではありません。
policy変更の検証方法
接続タイムアウトを変更したら、問題を安定して再現できる状況で検証してください。たとえば長時間接続を元のしきい値以上アイドル状態にしてからデータを送信し、接続が再構築されるかを確認します。Webページを素早く更新するだけでアイドルポリシーを判断しないでください。特定のプロトコルやアプリだけに異常がある場合は、その接続が変更したユーザーレベルに本当に割り当てられているかを確認します。クライアントが設定を生成する際に手動のpolicyを上書きすることもあるため、実行時設定またはログで最終値を確認してください。
policyは、明確なライフサイクルや統計の要件を解決するためのものであり、誤ったルーティング、トランスポート、リモートパラメータを修正するものではありません。接続が最初から確立できない場合は、outboundとトランスポート層に戻って確認します。特定のドメインだけ接続できない場合は、ルーティングとDNSを調べます。接続確立後、一定のアイドル時間で切断される場合にpolicyオブジェクトを確認してください。この順序なら、タイムアウトを緩めてハンドシェイク失敗を隠すことを避けられます。
streamSettings、トランスポート方式、セキュリティパラメータ
リモートプロトコルは認証情報とリクエスト形式を定義し、streamSettingsはそれらのデータをネットワーク接続上でどう運ぶかを定義します。両端のプロトコルパラメータが正しくても、トランスポート層が一致しなければ接続は完了しません。
networkが後続の設定オブジェクトを決める
streamSettings.networkはトランスポートの種類を指定します。一般的な設定にはTCP、WebSocket、gRPC、mKCPなどがあります。ネットワーク種別を選んだら、WebSocketならwsSettings、gRPCなら対応するサービス名設定など、該当する設定オブジェクトを使います。別のトランスポート用フィールドを残しても自動的に組み合わされるわけではなく、かえって設定を読み違える原因になります。ノードを移行するときは、まず画面に表示されたトランスポート種別を確認し、その専用フィールドを展開して一つずつ照合してください。
トランスポート種別は、クライアント側だけで最適化できる項目ではありません。サーバーが待ち受けるトランスポート、パス、サービス名に合わせて、クライアントも同じ構造で接続する必要があります。エラーが出たときにTCP、WebSocket、gRPCを頻繁に切り替えると、通常は変数が増えるだけです。元の設定に基づいてトランスポートを固定し、アドレスが解決できるか、ポートへ到達できるかを確認してから、パス、ホストフィールド、セキュリティ層を確認する方が安全です。
{
"streamSettings": {
"network": "grpc",
"security": "tls",
"tlsSettings": {
"serverName": "edge.example.com",
"allowInsecure": false,
"alpn": ["h2"]
},
"grpcSettings": {
"serviceName": "example-service",
"multiMode": false
},
"sockopt": {
"tcpKeepAliveIdle": 100
}
}
}
WebSocketのパス・Host・TLS名
WebSocket設定のpathはHTTPハンドシェイクのパスです。先頭にスラッシュを付けるか、クエリ部分を含めるかも、サーバー側の入口と一致させる必要があります。headers.HostはWebSocketハンドシェイクのヘッダーであり、tlsSettings.serverNameはTLSサーバー名の検証に使われます。両者は同じ場合もありますが、構成によって異なる場合もあります。1つのフィールドとして同時に置き換えると、一方だけ成功して他方が失敗する可能性があります。
サーバーアドレスaddressは最初に接続するホストを決め、TLS名は証明書とハンドシェイクの対象名を決め、WebSocketのHostはHTTP層で処理されます。確認時は、接続確立の順序に沿って3つを理解してください。アドレスへのTCP接続は確立できるのにTLSが失敗する場合は、システム時刻、サーバー名、セキュリティパラメータを重点的に確認します。TLS成功後にWebSocketが拒否される場合は、パスとHostを確認します。証明書検証を安易に緩めることはエラーを見えにくくするため、通常の修復方法にしないでください。
gRPC・mKCP・トランスポート固有フィールド
gRPC設定では通常、正確なserviceNameが必要で、HTTP/2関連のネゴシエーションにも依存します。サービス名は設定値であり、ノードのメモではありません。URL形式のスラッシュを自分で追加することも避けてください。中間層が必要な接続方式に対応していない場合、ハンドシェイク直後に切断されることがあります。mKCPはUDPベースのトランスポートであり、双方のパラメータだけでなく、現在のネットワークがUDPを安定して運べるかにも依存します。TCPへ到達できても、UDP経路が利用できるとは限りません。
トランスポート固有パラメータが多い場合は、まずクライアントにノード情報から生成させた構造を使い、明確な要件がある箇所だけを変更してください。インターネット上の異なるコアや時期の例には、すでに変更されたフィールド名が含まれていることがあります。v2rayNは通常デスクトップ用コアの管理に対応し、v2rayNGはXrayのコア経路、v2flyNGはV2Flyのコアを対象とします。同じ名前のトランスポートでも基本概念は近い一方、拡張フィールドが完全に一致するとは限りません。対応範囲が不明な場合は、不要な拡張を削除し、サーバーが要求する基本フィールドを残してください。
security・TLS・Realityの階層
securityは有効にするセキュリティ層を決めます。対応する設定オブジェクトも一致していなければなりません。TLSパラメータはtlsSettingsに置き、Realityパラメータはコアが定める独立した設定オブジェクトを使います。security文字列だけを置き換えて、古いフィールドをすべて流用することはできません。サーバー名、公鍵関連パラメータ、短い識別子、フィンガープリントの選択は、特定のセキュリティ方式に固有のハンドシェイク条件です。同じ有効な設定から取得してください。
セキュリティ層のエラーとユーザー識別子のエラーは、画面上ではどちらも接続テスト失敗に見えることがあります。そのためログでは、DNS、TCP、TLS、プロトコル認証のどの段階で発生したかを区別する必要があります。サーバーポートに接続すらできないなら、サーバー名を変更しても意味がありません。TCP接続が確立しているのにセキュリティハンドシェイクが失敗する場合は、先にルーティングを変更しないでください。障害の層を特定してからパラメータを変更すれば、エラーの形だけを一時的に変えて根本原因を残すことを避けられます。
| 階層 | 代表的なフィールド | リモート側と一致させる内容 |
|---|---|---|
| 接続先 | address、port |
ホストとリスニングポート |
| トランスポート | network |
トランスポート種別、パス、サービス名 |
| セキュリティ層 | security |
サーバー名と対応するハンドシェイクパラメータ |
| アプリケーションプロトコル | protocol、settings |
ユーザー識別子とプロトコル属性 |
設定の検証とトラブル対応を層ごとに進める方法
効果的なトラブル対応は、パラメータを次々に変更することではありません。接続がどの層を通過し、どの層で停止したかを確認することです。構造、リスニング、名前解決、ルーティング、outbound、アプリ設定を決められた順番で確認してください。
第1層:JSONを読み込めることを確認する
起動に失敗したら、まずクライアントまたはコアのログにある最初の構造エラーを確認し、行番号とフィールドパスを記録します。よくある問題は、カンマの欠落、括弧の種類の不一致、文字列の閉じ忘れ、フィールドを誤ったオブジェクトへ置くこと、数値を文字列にすること、標準JSONにコメントを入れることです。エディターが示す位置は、パーサーが後続文字を読むまで前の項目の不完全さを判断できず、実際のエラーの次の行になる場合があります。そのため、エラー行より前のオブジェクト末尾も確認してください。
構造が正しいからといって、フィールドの意味まで正しいとは限りません。正しく綴られたJSONでも、コアが認識しないフィールド、誤ったプロトコル設定、存在しないタグを含むことがあります。この場合は、ログのオブジェクトパスから該当する章へ戻り、そのフィールドがinbound、outbound、トランスポート設定のどれに属するか確認してください。クライアントが起動のたびに設定を書き換えるなら、編集しているのは生成物です。クライアント画面またはカスタム設定の入口へ戻り、元の設定を変更してください。クライアントを初めて使う場合の操作手順は入門ガイドで確認できます。
{
"log": {
"access": "",
"error": "",
"loglevel": "warning"
}
}
第2層:接続が正しいinboundへ入っていることを確認する
設定の読み込み後、ローカルのリスニングアドレスとポートが現れているか確認します。アプリのプロキシアドレスはinboundと一致させ、SOCKSとHTTPの種類も混同しないでください。システムプロキシを有効にしてもアプリが直接接続する場合は、そのアプリがシステム設定を読み取るか確認します。逆に、クライアントでシステムプロキシを無効にしても、ブラウザー拡張機能が独自のプロキシを保持していれば、通信が古いポートへ入ることがあります。トラブル対応中は入口を1種類だけにすると、二重プロキシや経路不明の問題を大幅に減らせます。
ポートが待ち受けていない場合は、まず使用中のポートがないか、クライアントのコアが起動しているか、別のプロセスが設定を読み込んでいないか確認します。ポートは存在するのにアクセス記録がまったくない場合、問題は通常アプリとinboundの間にあります。ログに宛先アドレスが記録されていれば入口は成立しているため、ルーティングとoutboundの確認へ進めます。モバイルではVPNサービスの状態とアプリ別プロキシの範囲も確認してください。対象から除外されたアプリは、v2rayNGやv2flyNGのコア経路を通りません。
第3層:DNS・ルーティング・outboundタグを追跡する
固定したテストドメインへリクエストを送り、コアがドメイン名を取得したか、解決を実行したか、どのルールに一致したか、どのoutboundを選んだかを順に記録します。ドメインだけ失敗してIPは成功する場合はDNSとドメインルールを重点的に確認します。すべての宛先が誤ったoutboundへ入る場合は、広すぎるルールとデフォルト経路を確認します。特定のアプリだけ失敗する場合は、inboundタグ、ネットワーク種別、スニッフィング結果を比較してください。変化し続ける複数のテストサイトを同時に使うと、キャッシュ、アドレスファミリー、ドメインルールの違いが余計な変数になるため避けます。
ルーティングが正しく一致しているのにリモートoutboundが失敗する場合は、アドレス解決、ポート到達性、プロトコルのユーザーパラメータ、トランスポート種別、セキュリティ層を確認します。トラブル対応中はログレベルを適度に上げても構いませんが、確認後は日常利用に適したレベルへ戻してください。大量のログが重要なイベントを隠すことがあります。ログには対象ドメイン、内部アドレス、ローカルパスが含まれる可能性があるため、共有前に問題と無関係な個人設定を削除してください。
第4層:クライアントの状態とコアの状態を区別する
v2rayN、v2rayNG、v2flyNGはいずれもクライアント管理層を持ち、コアは接続処理を担う一部にすぎません。サブスクリプション更新が成功しても、すべてのノードが接続できるとは限りません。遅延テストの失敗も、すべてのアプリの接続失敗と同じとは限りません。テスト方法、対象アドレス、実際のアプリプロトコルが異なるためです。「サブスクリプションが更新されたか」「ノードが選択されているか」「コアが起動しているか」「システムまたはVPNの入口が有効か」「実際のリクエストが通過したか」を分けて判断してください。
v2rayNのAvaloniaデスクトップ版はWindows、macOS、Linuxに対応し、WPF版はWindows専用です。2つのリリース系統では、画面やシステム統合に違いがあります。デスクトップ版を選ぶときは、v2rayNデスクトップ版とWPF版の比較を参照してください。Linuxのインストールとログインセッション開始時の設定は、Linuxでv2rayNをインストールする手順で確認できます。プラットフォームの違いは主にクライアント外側の操作に影響するもので、ノードのプロトコルパラメータを根拠なく変更してはいけません。
元に戻せる変更履歴を作る
各ラウンドでは1つの論理層だけを変更し、変更前の動作するコピーを保存してください。ローカルの記録は「構造整理、inbound、DNS、ルーティング、outbound、トランスポート、policy」の順で名前を付け、期待した結果と実際のログも書き留めます。1回の変更に複数のフィールドを含めると、接続が回復しても本当の原因を特定できず、後の移行で同じ問題が再発しやすくなります。設定を戻すときは同じラウンドのバージョン全体を復元し、古いルーティングと新しいタグを混在させないでください。
サブスクリプションで管理するノードは、まずクライアントが提供する編集入口で調整し、更新時にローカル変更が上書きされるか確認してください。長期的に保持するルーティングやDNSルールは、クライアントが対応するカスタム設定層へ入れるのが適切です。一時的な実行ファイルを何度も編集するのは避けます。設定が大きくなったら、参照されていないタグ、重複ルール、期限切れの実験用inboundを定期的に削除し、ログ上の各経路が現在の用途と対応する状態を保ってください。
参考設定からクライアント操作へ戻る
最小構成で動作したら、ドメインルール、DNS分岐、複数のoutbounds、policy設定を順に戻し、グループを1つ戻すたびに同じテストを繰り返してください。特定のグループを戻した後だけ問題が出るなら、確認範囲をそのグループと参照先に絞れます。クライアントを再インストールまたは切り替える場合は、クライアントのダウンロードページでWindows、macOS、Android、Linuxに対応する入口を選んでください。ダウンロードページのよくある質問では、インストールパッケージの選択、プロセッサーアーキテクチャ、バージョン切り替えについても説明しています。
設定ファイルは、相互に参照し合う宣言の集合であり、孤立したパラメータの集まりではありません。inboundタグはルーティング条件に影響し、ルーティングポリシーがDNSを呼び出し、DNS問い合わせにはoutboundが必要で、最終的にはトランスポートとセキュリティ層がリモート接続の成否を決めます。依存関係に沿って読むことで、「接続できない」という問題を、構造、入口、名前解決、ルール、ハンドシェイクのいずれか検証可能な問題へ分解できます。記録を残した層別トラブル対応を一度行えば、以後のノード移行やクライアント更新にも同じ方法を適用でき、無作為な変更からやり直す必要がなくなります。