JSONの構造と設定の生成元
まず設定の生成元を確認してから、変更する場所を決めましょう。v2rayN、v2rayNG、v2flyNGはいずれもGUIクライアントで、ノード、サブスクリプション、ルーティング、ローカル設定をまとめ、コアの実行に必要な設定を生成します。クライアント画面に保存された情報、サブスクリプションから取得したデータ、実際に動作しているJSONは、それぞれ異なる階層のものです。サブスクリプションURLは設定ファイルではなく、クライアントのデータベースもコアの設定と同じものではありません。問題を調べる際は、過去に書き出したファイルだけでなく、現在の接続で実際に使われている設定を確認してください。
トップレベルのオブジェクトとリクエストの流れ
一般的な設定では、1つのオブジェクトをルートにします。オブジェクト内のキーは機能モジュールを表し、値はオブジェクトまたは配列です。同じプロセスで複数の入口や出口を使えるため、inboundsとoutboundsは配列です。一方、routingはオブジェクトで、その中のrulesが順序を持つルール配列です。項目の並び順を覚えるより、階層を理解することが重要です。トップレベルのキーは通常、記述順に依存しませんが、ルール配列やoutbounds配列の順序は実際の動作に影響することがあります。
{
"log": {
"loglevel": "warning"
},
"inbounds": [],
"outbounds": [],
"routing": {
"domainStrategy": "AsIs",
"rules": []
},
"dns": {
"servers": ["localhost"]
},
"policy": {
"levels": {}
}
}
上記は構造の骨組みであり、通信を転送できる設定ではありません。inboundsが空ではリクエストを受け付けるポートがなく、outboundsが空では転送先がありません。実際のリクエストは通常、「アプリ→inbounds→ルーティング→outbounds」の順に流れ、途中でDNSによる名前解決が行われることがあります。ログは処理中のイベントを記録し、policyは接続リソースやタイムアウトに影響します。各モジュールは独立したスイッチではありません。正しく記述されていても参照されていないoutboundタグが、トラフィックを自動的に処理することはありません。
構文の正しさと設定の意味上の正しさ
標準JSONでは、キーと文字列を半角の二重引用符で囲みます。真偽値は小文字のtrue、falseで記述し、数値には通常引用符を付けません。配列やオブジェクトの最後の要素の後ろにカンマを置くことはできません。また、説明文をコードに直接書き込まないでください。一部の実装では拡張構文を使えますが、異なるクライアント間で設定をコピーする場合は標準JSONに従いましょう。重複キーは特に誤解を招きやすく、解析方法によってどの値が残るかが異なることがあります。同じオブジェクト内に同名の項目を複数記述しないでください。
構文解析に成功しても、括弧、型、文字が正しいと確認できただけで、プロトコルの設定が有効とは限りません。コアはプロトコル名、対応する項目、タグ参照、待ち受けリソースなども確認します。正常に起動しても、リモートアドレス、認証情報、トランスポートの設定が一致しなければ接続に失敗することがあります。「ファイルを解析できる」「プロセスが起動する」「リクエストが通る」「ルールが想定どおりに適用される」は別々の確認事項です。1つずつ確認し、Webページを一度開けたかどうかだけで全体の状態を判断しないでください。
| 設定セクション | 主な役割 | 最初に確認する項目 |
|---|---|---|
inbounds | アプリからのリクエストを受け付ける | 待ち受けアドレス、ポート、プロトコル |
outbounds | 転送または拒否を処理する | outboundの種類、タグ、リモート設定 |
routing | 条件に応じて接続先を選ぶ | ルールの順序、タグ参照 |
dns | 内蔵DNSによる名前解決 | 問い合わせ経路、名前解決結果 |
policy | 接続リソースとタイマーを制御する | レベルの関連付け、タイムアウトの範囲 |
コアの違いと設定変更の範囲
Project Vは関連ツールのエコシステムを理解するうえでの背景です。V2FlyとXrayは、それぞれ別のコア実装として区別する必要があります。共通する設定概念はありますが、新しい項目、トランスポートの組み合わせ、デフォルト値がすべて同じとは限りません。特にREALITYなどの拡張機能は、実際に使うコアに対応した項目で設定してください。特定の実装専用の設定セクションを、別の実装のファイルにそのまま統合することはできません。このページの例は一般的な基本構造を使っています。拡張機能を使う場合は、クライアントで選択中のコアを確認してください。
GUIクライアントは、ノードの切り替え、サブスクリプションの更新、再接続などのタイミングで設定を生成することがよくあります。一時生成されたファイルを直接編集すると、次回の操作で上書きされる可能性があります。変更を長期的に保持するには、クライアントが用意するルーティング、DNS、自定义設定の入力欄を優先し、各設定がどのように統合されるか確認してください。変更前の情報を別に保存し、変更した項目と想定する結果を記録しましょう。一度に変更する階層を1つに絞れば、問題が起きても正確に元に戻せます。クライアント選びの最初の一歩にはクライアントとプラットフォームのダウンロードページをご覧ください。
log(ログ)と確認方法
ログは特定の実行時に何が起きたかを調べるためのもので、すべての接続が正常であることを証明するものではありません。設定の起動に失敗した場合は、まずエラーログを確認します。アプリがローカルポートには接続できるのにリクエストを完了できない場合は、アクセスログ、接続先アドレス、outboundの動作もあわせて確認してください。コア、クライアント画面、システムサービスのログは、別々のプロセスから出力されている場合があり、表示時刻や詳細度も異なります。2つのログを比較する前に、同じ起動時の記録であることを確認し、古いエラーを現在の問題と取り違えないようにしてください。
ログレベルと出力先
loglevelは、エラー関連ログの詳細度を制御します。一般的な値はdebug、info、warning、error、noneです。通常は控えめなレベルにしておき、問題を再現するときだけ一時的に詳細度を上げましょう。詳細度を上げても観察できる情報が増えるだけで、ルールの照合結果が変わったり、ハンドシェイクの問題が直ったりするわけではありません。特定のアプリだけで問題が起きる場合は、すべての通信を長時間記録するのではなく、まず再現条件を絞り込んでください。
{
"log": {
"access": "./access.log",
"error": "./error.log",
"loglevel": "warning"
}
}
この設定はトップレベルのオブジェクトに統合します。2つのパスはいずれも相対パスなので、実際の出力先はプロセスの作業ディレクトリによって決まり、JSONファイルと同じディレクトリとは限りません。デスクトップクライアントをショートカットから起動する場合、サービスとして起動する場合、ターミナルから起動する場合では、作業ディレクトリが異なることがあります。ファイルが見つからない場合は、まず起動方法とディレクトリのアクセス権を確認し、その後でログが無効になっていないか調べてください。ログレベルを何度も上げるより、書き込み可能な場所にパスを変更するほうが直接的です。この目的でプログラム全体のアクセス権を広げないでください。
クライアントがすでにコアの出力を収集している場合、ログファイルを別途指定する必要はありません。ファイルを指定しない場合の出力先は、実際のコアと起動方法に応じて確認してください。画面に概要だけを表示するクライアントもあれば、アクセスログを個別に有効化できるクライアントもあります。ログファイルが増え続ける場合は、実行環境側でローテーションや削除の方法を用意してください。コアが自動でファイルサイズを制限するとは限りません。記録の保存期間はトラブルシューティングに必要な範囲にとどめ、再現確認が終わったら元のログレベルに戻して、長期間の記録でディスクがいっぱいにならないようにしましょう。
時系列で最小限の証拠を集める
問題を有効に再現するには、明確な開始点と終了点が必要です。まず既存の接続を停止し、再起動した時刻を記録してから、接続先が分かっているアドレスに1回リクエストを送ります。inboundsでの受信、接続先の特定、ルールの選択、outboundのエラーが記録されるか確認してください。実装によっては、これらの情報がすべて出力されるとは限りません。ログが同じ文言で記録されることを前提にせず、時刻、接続先、結果を関連付けて調べましょう。複数のページを続けて更新すると、先行接続、バックグラウンド通信、キャッシュが加わり、ログとリクエストの対応が分かりにくくなります。
よくあるエラーは、発生した階層ごとに考えると整理しやすくなります。待ち受けの失敗は、ポートの競合、存在しないアドレス、権限などが原因として考えられます。名前解決の失敗はDNS経路を、リモート接続のタイムアウトは接続先への到達性やトランスポート条件を示唆します。認証やハンドシェイクのエラーでは、サーバーとクライアントの設定を比較してください。最終的にタイムアウトが表示されても、それだけで問題の発生箇所は特定できません。最後の1行をコピーするだけでなく、その前に出た最初の意味のあるエラーを探しましょう。再試行で繰り返し記録された同じ情報を、別々の原因と見なさないでください。
ログがない場合と通信が届いていない場合を区別する
アクセス記録がない場合は、まずアプリが本当にそのinboundを使っているか確認してください。ブラウザーはシステムプロキシを参照することがありますが、コマンドラインツールは独自の引数しか見ないことがあります。既存の接続が以前の経路を使い続けている可能性もあります。テスト時に接続を作り直し、プロキシアドレスを明示すると、変数を減らせます。アプリのリクエストがコアに届いていない場合、outbound、DNS、ルーティングを変更しても結果は変わりません。システムプロキシとコアのルーティングの違いは、システムプロキシとルーティングモードの解説をご覧ください。
リクエストが届いているのに、記録されたoutboundが想定と異なる場合は、ルーティングの章でルールの順序とタグを確認してください。outboundが正しく、一部の接続先だけ失敗する場合は、アドレスファミリー、トランスポートプロトコル、名前解決経路の違いを比較します。ログは手がかりの1つです。再現可能なテストとあわせて確認してください。成功時と失敗時の記録を1つずつ保存し、違いを順に比較すると、ポート、リゾルバー、ルーティングモードを同時に変更するより原因を特定しやすくなります。
inbounds(ローカル接続の受付)
inboundは、コアがどこでリクエストを受け付けるか、アプリから受け取ったデータをどのプロトコルとして扱うかを定義します。デスクトップ環境で一般的なのは、ローカルのSOCKSまたはHTTPプロキシです。ポートは入口を識別する番号であり、outboundがリモートノード経由になるかどうかを決めるものではありません。また、すべてのアプリが自動的にプロキシを使うわけでもありません。アプリがそのポートに接続するか、システムプロキシなどの仕組みを通じてリクエストを渡す必要があります。問題を調べる前にアプリが必要とするプロトコルを確認し、SOCKSのポートをHTTPプロキシ専用の設定欄に入力しないようにしてください。
待ち受けアドレス、ポート、タグ
listenはバインドするアドレス、portは待ち受けポート、protocolはinboundのプロトコル、tagは後続の設定から参照するための識別子です。ローカルでのみ使う場合は、127.0.0.1を明示してアクセス範囲を制限できます。このアドレスは本機のIPv4ループバックを示し、すべてのネットワークインターフェースやIPv6ループバックを示すものではありません。アプリが別のアドレスファミリーを使う場合は、双方のアドレスを確認してください。「localhost」という名前だけで接続先を判断しないようにしましょう。
{
"inbounds": [
{
"tag": "local-socks",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
]
}
この例では、認証なしのSOCKS inboundをローカルアプリに提供し、UDP転送を有効にしています。ここでのudpは、システム内のすべてのUDP通信が捕捉されるという意味ではありません。アプリがSOCKSの対応機能を使い、outboundも該当する通信を処理できる必要があります。Webページだけをテストして成功しても、通常はそのTCPリクエストが通ったことしか確認できません。音声、リアルタイム通信、その他のUDPアプリについては判断できません。動作に違いがある場合は、アプリの対応状況、inboundの機能、outboundの経路を個別に確認してください。
LANからの接続とセキュリティ範囲
LANから接続できるようにするには、待ち受けアドレスを0.0.0.0に変えるだけでは不十分です。ホストのファイアウォール、ネットワークインターフェース、認証方法、接続元の制限も考慮してください。すべてのインターフェースで待ち受けると、アクセス可能な範囲が広がり、別のネットワークに接続したときには公開範囲も変わります。特定のLANインターフェースだけが必要な場合は、そのインターフェースのアドレスにバインドし、ファイアウォールで接続元を制限できます。明確な共有目的がなければ、ループバックで待ち受けるほうが管理しやすく、他の端末に誤って使われることも防げます。
同じアドレス、同じトランスポート種別のポートを複数のプロセスが同時に使用することはできません。クライアントのプロセスがバックグラウンドに残っている、別のツールが同じポートを使っている、設定を2つ同時に起動しているといった理由で、バインドに失敗することがあります。まず不要なプロセスを終了し、ポートを使っているプロセスを確認してから、必要であればポートを変更してください。inboundのポートを変えた場合は、アプリのプロキシ設定やシステムプロキシも更新します。JSONだけ変更してプロキシアドレスを古いままにすると、プロトコルのハンドシェイクエラーではなく、接続拒否として現れることがよくあります。
スニッフィングによる識別と接続先の上書き
スニッフィングは、リクエストの内容からドメインなどの接続先情報を識別しようとします。たとえば、アプリがドメインをIPアドレスに名前解決してからIPをinboundに渡す場合、HTTPヘッダーやTLSハンドシェイクの情報を使ってドメインを復元できることがあります。これによりドメインルールでルーティングできる場合がありますが、識別できるかどうかは実際のプロトコル内容に左右されます。暗号化された通信のドメインを必ず復元できるわけではありません。識別できる情報がない場合、接続先が特殊なハンドシェイクを使う場合、対応範囲外のプロトコルの場合は、IPなど他の条件だけでルーティングされることがあります。
destOverrideは、識別結果を接続先にどう反映するかに関わります。「ドメインを識別すること」と「ルーティングの判定にだけ使うこと」は同じではありません。コアによって制御項目やデフォルトの動作が異なる場合があります。接続先を変更せずマッチングにだけ反映したい場合は、実際の実装で対応する機能を確認してから設定してください。スニッフィングを有効にした後にアクセス結果が変わった場合は、同じ接続先を使って無効時と有効時を比較し、接続先とルールの変化を確認しましょう。ノードとDNSを同時に変更しないでください。
用途別に複数のinboundを設定できます。たとえばテストリクエスト専用のタグを設け、inboundTagから専用のoutboundを指定できます。タグは論理的な名前であり、ポートを作成したり、システムプロキシを自動的に有効化したりするものではありません。用途が分かる名前を付け、区別しにくい番号を大量に使うのは避けましょう。待ち受けを確認してからスニッフィングやルーティングを追加すれば、入口の問題と後続のポリシーの問題を切り分けられます。GUIの基本操作についてはクイックスタートの手順をご覧ください。
outbounds(接続先の選択)
outboundは、選択されたリクエストをどのように処理するかを決めます。接続先へ直接接続する、指定したプロキシプロトコルでリモートへ転送する、接続を拒否するといった処理ができます。outboundは単に「ノード一覧」を指す言葉ではありません。direct接続や拒否もoutboundに含まれ、リモートサーバーを必要としないものもあります。設定に複数のoutboundがあっても、デフォルト経路またはルーティングから参照されたものだけがリクエストの処理に使われます。まずどのoutboundが選択されたかを確認してから、その設定を調べると問題を整理しやすくなります。
基本的なoutboundとデフォルト動作
一般的な設定では、ルーティングで接続先が指定されない場合、outbounds配列の先頭にあるoutboundがデフォルトとして使われます。そのため配列の順序を変えると、ルールに一致しないトラフィックの行き先が変わることがあります。デフォルトoutboundの用途を明確にし、参照する各outboundには一意のタグを付けてください。設定内でタグの表記は統一し、大文字・小文字や文字も一致させます。タグは画面に表示される名称と必ずしも同じではなく、ノードの表示名から実際に生成されたタグを推測することもできません。
{
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "AsIs"
}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
]
}
この例ではdirectを先頭に置いているため、追加のルールがなければリクエストは本機のネットワークから直接送信されます。blackholeは通信を拒否する処理で、ルーティングからblockを指定した場合にだけ使われます。予備の経路ではなく、directが失敗したときに自動で切り替わることもありません。outboundを2つ配列に記述しても、フェイルオーバーが構成されるわけではありません。負荷分散、接続確認、チェーン接続にはそれぞれ別の機能と明示的な設定が必要です。
プロトコル設定とトランスポート設定を分けて考える
リモートプロキシのoutboundには、通常、プロトコル層とトランスポート層の設定が含まれます。前者はサーバー、ポート、認証、プロトコル固有の機能を指定し、後者は下位の接続方式とセキュリティ設定を指定します。サーバー側とクライアント側では、重要な項目を一致させる必要があります。リモートポートに接続できても、ネットワーク層で接続を確立できたことしか確認できず、認証やトランスポートの組み合わせが正しいとは限りません。ハンドシェイクに失敗した場合は、見慣れたデフォルト値にすべて置き換えるのではなく、提供元の完全な設定と照合してください。
VMess、VLESS、Trojan、Shadowsocksは認証方式と機能が異なるため、protocolの名前だけを変えて相互に変換することはできません。TLSやREALITYなどの組み合わせも、コアの対応範囲を踏まえて検討してください。プロトコルの選び方については、4種類のプロキシプロトコルを用途別に比較をご覧ください。このページでは、架空のリモート認証情報を使った「万能ノード」は掲載していません。完全な設定例でdirectを使うのは、ローカルの構造上の問題を切り分けるためであり、実際のリモート経路を再現するためではありません。
ドメインの接続先と接続処理の担当
outboundが受け取ったドメインを本機で名前解決するか、内蔵DNSで解決するか、ドメインのままリモートへ渡すかは、outboundのプロトコルと関連設定によって異なります。freedomのdomainStrategyとrouting.domainStrategyは名前が似ていますが、設定の階層は異なります。前者はdirect outboundでドメインの接続先をどう扱うか、後者はルーティングの照合時に名前解決を行うかどうかに影響します。ルーティングのポリシーをIP解決に変更しても、すべてのoutboundが同じ方法で名前解決するわけではありません。
複数のoutboundを使う設定では、最終的な接続先とプロキシサーバー自体のアドレスも区別してください。サーバーのドメインも名前解決して接続する必要があります。その通信を、サーバー自身に依存する経路へ再び送ると、循環依存が発生する可能性があります。GUIクライアントは必要な除外設定やdirect接続を処理することがありますが、カスタム設定では初回接続がどの経路から確立されるかを明確に確認してください。ハンドシェイクが完了する前に何度もタイムアウトする場合は、まずサーバーアドレスの名前解決、下位の到達性、outboundの選択を確認しましょう。
| 観察された現象 | 最初に確認すること | すぐに結論づけないこと |
|---|---|---|
| ローカルには接続できるが、リモートで失敗する | outboundタグ、サーバー設定、トランスポート条件 | DNSが原因とは限らない |
| ルールに一致しない | デフォルトoutboundと配列の順序 | すべてのoutboundを順番に自動で試すわけではない |
| directは使えるが、プロキシ接続に失敗する | リモート経路とプロトコルの組み合わせ | inboundの設定に誤りがあるとは限らない |
ノードの設定を変更する場合は、サブスクリプション更新で手動入力が上書きされないよう、クライアントのノード編集画面を優先してください。固定のoutboundタグを使うカスタムルールでは、クライアントが生成するタグが安定しているか、ノードの切り替え後も参照できるかを確認します。タグが再生成される場合は、実行中の設定ファイルをコピーするより、クライアントのルーティング設定を使うほうが確実です。動作確認済みの基本設定を比較用に残しておくと、「outbound自体の失敗」と「ルーティングの選択ミス」を別々に検証できます。
routing(ルールの照合)
ルーティングはルールを順に確認し、一致した結果を指定のoutboundに渡します。システムプロキシのスイッチではなく、コアに届いていないアプリの通信を取り込む機能でもありません。ルールが正しいのにアプリが従来のネットワークを使う場合は、まずinboundを確認してください。リクエストがコアに届いているのにoutboundが違う場合に、ルールの順序、照合項目、タグ参照を確認します。入口で通信を取り込む処理と、コアがoutboundを選ぶ処理を分けて考えることが、「グローバル」や「中国本土をバイパス」といった画面モードを分析する前提です。
ルールの順序、条件、接続先情報
1つのルールに複数の異なる条件を指定した場合、通常はすべて満たす必要があります。たとえばinboundタグを指定し、さらにポートを制限する場合です。同じ種類の配列内に複数の候補がある場合は、そのいずれかが一致すれば条件を満たします。ルールに一致すると、後続の通常ルールで上書きされることはありません。広い範囲に一致するルールは、細かな例外より後ろに配置してください。先に置くと、例外が適用されなくなります。ドメイン条件には利用可能なドメイン情報が必要で、IP条件には接続先アドレスまたは名前解決結果が必要です。IPしかないリクエストがドメインリストにも自然に一致するとは限りません。
{
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": ["full:blocked.example"],
"outboundTag": "block"
},
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"inboundTag": ["local-socks"],
"network": "tcp,udp",
"outboundTag": "direct"
}
]
}
}
この例では、専用のテストドメインを最初に拒否し、次にプライベートアドレスを処理し、最後に指定したinboundからの残りのTCP・UDPリクエストをdirectに渡します。blocked.exampleは説明用のドメインで、差し替えが必要な認証情報ではありません。2つのoutboundタグは前の章で定義したものと一致させてください。3つ目のルールは広い範囲に一致するため、特定の接続先の例外を追加する場合は、通常その前に挿入します。後ろに記述すると構文が正しくても、ルールが適用される機会がないことがあります。
ドメインの記法とデータファイル
full:はドメイン全体との完全一致に使い、domain:はドメインとそのサブドメインを含む一致に使います。regexp:は正規表現による条件を表します。接頭辞を付けない場合にも独自の一致ルールがあるため、一律に完全一致と見なすことはできません。意図しない一致を避けるには、複雑な正規表現で明確な完全一致やドメインサフィックスを表現するのではなく、意図に合った接頭辞を選んでください。正規表現にはJSON文字列のエスケープ規則も適用されるため、式中のバックスラッシュも文字列の規則に従って記述します。
geoip:とgeosite:は、対応するルールデータを参照します。実行時にオンライン検索を行うものではありません。使用前に、コアが必要なデータファイルを読み込めることと、参照するカテゴリがファイルに含まれていることを確認してください。カテゴリ名が存在しても、すべての接続先が対象になるとは限りません。データの更新で一致範囲が変わることもあります。サイトの通信が想定外のoutboundに振り分けられた場合は、実際のドメイン、関連リソース、データ内容を比較してください。1つのWebページには個別のリクエストが複数含まれることが多いため、アドレスバーのドメインだけを見て判断しないでください。
domainStrategyによる名前解決のタイミング
AsIsは、すでにある接続先情報を使って照合し、IPルールのために積極的な名前解決を行いません。IPIfNonMatchは、ドメイン段階で適用できるルールが見つからない場合に名前解決を試し、IP条件で照合します。IPOnDemandは、照合にIP情報が必要になった時点で名前解決します。これらが変えるのは、ルールの照合中に情報を取得する方法です。「高速」「高機能」といった一律の優劣を表すものではありません。早い段階で名前解決すると依存関係が増え、十分に名前解決しないと想定したIP分類を適用できないことがあります。
特に注意が必要なのが、最後に置く包括的なルールです。先行するルールがすべてのリクエストに一致する場合、IPIfNonMatchではその時点で結果が決まり、期待していた名前解決の段階に進まないことがあります。この場合、DNSが動作していないのではなく、ルールの照合が早く終了しています。1つの項目を切り替えるだけではなく、ルールの順序を考慮して名前解決の方針を決めてください。プライベートアドレスのルールも、コアが実際に把握しているアドレスに対してのみ一致します。ドメインがプライベートアドレスに解決される場合は、解決のタイミングを個別に確認しましょう。
クライアントが生成する「中国本土をバイパス」は、通常、ルールセットを指します。すべてのコアに共通する単一の設定値ではありません。設定テンプレートによって、ドメインリスト、IP分類、デフォルトoutboundが異なることがあります。実際の動作を説明するには、モード名だけでなく、現在適用されているルールを確認する必要があります。用途に応じたモード選びは、システムプロキシとグローバルモード・中国本土をバイパスするモードの解説をご覧ください。速度に差が出る場合は、ルール選択、名前解決経路、リモート経路を分けて確認し、すべてをモード名だけの問題と見なさないようにしてください。
dns(名前解決)
DNS設定では、まず「誰が問い合わせるのか」「問い合わせはどこから送信されるのか」「結果を誰が使うのか」を確認します。OSのリゾルバー、ブラウザー独自の名前解決機能、コア内蔵DNS、リモートサーバーによる名前解決が同時に使われる場合があります。コアのdnsを変更しても、すべてのアプリの名前解決方法が自動で置き換わるわけではなく、システム上の問い合わせがすべてコアに取り込まれるわけでもありません。関連するリクエストが実際に内蔵リゾルバーを使う場合に限り、ここでの設定が結果に影響します。
基本サーバーと静的マッピング
serversには、内蔵DNSが使うサーバーや関連設定を記述します。hostsでは静的なドメインマッピングを定義できます。以下の例では、テスト用ドメインをループバックアドレスに割り当て、その他の問い合わせは本機の名前解決機能に渡します。静的マッピングは検証の切り分けには便利ですが、頻繁に変わるサービスのアドレスを長期的に固定する用途には適しません。サービスは地域、負荷、アドレスファミリーに応じて異なる結果を返すことがあります。アドレスを固定すると、そうした選択が反映されず、サービス側の変更後に利用できなくなる可能性があります。
{
"dns": {
"hosts": {
"full:manual.example": "127.0.0.1"
},
"servers": [
"localhost"
]
}
}
この設定を追加しても、すべてのブラウザーでテスト用ドメインが必ずループバックアドレスに解決されるわけではありません。対象の通信が実際にコア内蔵DNSを使うことが前提です。direct outboundがOSの名前解決経路を使う場合や、アプリがすでに名前解決を済ませている場合は、想定とは異なる結果になることがあります。そのため、静的マッピングを検証する際は、問い合わせ経路もあわせて確認してください。テスト後は設定を削除し、ループバックへの接続失敗を実際の接続先の問題と誤認しないようにしましょう。本番環境のマッピングには明確な用途と、変更履歴を追える管理記録が必要です。
ルーティング時の名前解決とoutboundでの名前解決
ルーティングの段階でIPルールに照合するためドメインを名前解決しても、outboundではドメインのまま接続することがあります。ルーティングのための名前解決と、最終的な接続で使うアドレスが同じとは限りません。direct outboundが内蔵DNSを使うかどうかはdomainStrategyによって異なります。プロキシoutboundで接続先をローカル解決するかどうかは、プロトコルと具体的な設定によります。リクエストを調べる際は、ルーティング判定に使った情報と、最終的な接続を確立したoutboundで使った情報を分けて記録してください。名前解決ログの一か所だけを見て判断しないでください。
アプリが先に名前解決を行い、IPアドレスをSOCKS inboundに渡す場合もあります。その場合、コアは元のドメインを把握できないことがあります。スニッフィングで補える場合もありますが、必ず成功するわけではありません。コア側のドメイン処理を検証するには、ドメインをプロキシに渡せるテスト方法を使い、アプリが事前にアドレスへ置き換えていないことを確認してください。ローカルでの名前解決とプロキシ側での名前解決を比較する場合も、ブラウザー、システム、クライアントのキャッシュを推測するのではなく、この変数を明示的に制御しましょう。
名前解決の通信経路と循環依存
名前解決サーバーのアドレスを指定しただけで、問い合わせが自動的にリモートプロキシ経由になるわけではありません。DNSサーバーの設定形式によって通信経路が異なることがあるため、それぞれ確認してください。接続に先立ってドメインの名前解決が必要な暗号化DNSエンドポイントを使う場合は、最初の名前解決をどう行うかも考慮します。DNSサーバーへの接続がプロキシに依存し、そのプロキシサーバーのドメインが同じDNSサーバーに依存していると、最初の接続を確立する前に循環依存が生じることがあります。起動時の依存関係が明確で、到達可能な経路を用意してください。
複数のリゾルバーとルーティング方針を一度に変更しないでください。まず現在返されるアドレスを記録し、問い合わせの経路を1つだけ変更して、同じ接続先で再テストします。複数のDNSサーバーを記述しても、必ず記述順に1つずつ試されるとは限りません。実際の動作には、照合条件、並列処理、フォールバック、実装ごとの差が関係することがあります。ドメインに応じて問い合わせ先を選ぶ場合は、現在のコアが対応するサーバーオブジェクトの項目を確認してください。他のツールの「優先」「代替」の意味を、そのまま当てはめないようにしましょう。
| 現象 | 関係する可能性のある階層 | 確認方法 |
|---|---|---|
| DNSを変更してもアドレスが変わらない | アプリのキャッシュ、システムの名前解決、既存の接続 | 問い合わせが内蔵リゾルバーに届いているか確認する |
| ドメインでは失敗するが、IPアドレスでは接続できる | 名前解決の失敗、または接続先名に関するハンドシェイク | 名前解決とサーバー名の要件を切り分ける |
| 一部の接続先への接続に時間がかかる | アドレスファミリー、取得したアドレス、回線の条件 | 名前解決と接続確立の各段階を個別に確認する |
アドレスファミリーとキャッシュの範囲
IPv4とIPv6の両方の結果がある場合、実際の動作は本機のネットワーク、outboundの対応状況、接続先の選択方法にも左右されます。接続に時間がかかっただけで、すぐに一方のアドレスファミリーを無効にしないでください。まず、返されたアドレス、失敗した接続、失敗後にフォールバックが行われたかを確認しましょう。名前解決の成功はアドレスを取得できたことを示すだけで、そのアドレスに接続できるとは限りません。また、IPアドレスに直接接続できても、TLSのサーバー名や仮想ホストが元のドメインを必要とする場合があります。
キャッシュはテストを安定させる一方で、設定変更を分かりにくくすることもあります。変更後は新しいリクエストで確認し、必要に応じてアプリの方法に従って該当するキャッシュを消去してください。コアの再起動前後の記録も区別しましょう。すべてのソフトウェアの再起動を診断の唯一の手段にしないでください。キャッシュがどの階層にあるかを特定すると、問題が発生する理由を説明しやすくなります。全体的に速度が低下している場合は、速度低下の段階的なトラブルシューティングもご覧ください。名前解決の待ち時間とリモート転送速度を分けて測定できます。
policy(接続ポリシー)
接続ポリシーはタイマーやリソースを管理するもので、ルーティングを選択したり、アプリ独自のタイムアウトを置き換えたりするものではありません。接続が頻繁に切れる場合、まずすべてのタイムアウトを延ばすのは避けてください。ハンドシェイクが未完了なのか、接続後に長時間アイドル状態だったのか、片側が送信を終了したのかを確認しましょう。状況ごとに関係する項目は異なります。アプリ、OS、リモート機器にも、それぞれ独自のタイマーが設定されていることがあります。切断がどの段階で起きているかを確認してから、目的を明確にしてポリシーを変更してください。
レベル設定とユーザーの関連付け
policy.levelsは、レベル名を使ってポリシーをまとめます。JSONオブジェクトのキーは文字列として記述するため、たとえば"0"のように書きます。ユーザーレベルに対応するプロトコル設定では、関連するlevel項目からポリシーを関連付けられます。具体的に適用される対象は、プロトコルの実装と実際に生成された設定によって異なります。ポリシーにレベルを記述しただけで、すべての接続に自動適用されるわけではありません。問題を調べる際は、接続に対応するユーザーのレベルと、明示的な指定がない場合のデフォルト動作を確認してください。
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5
}
}
}
}
この例の時間単位は秒です。接続の各段階に適用する制限を示すもので、すべてのネットワーク環境に推奨される値ではありません。handshakeは関連するハンドシェイク段階で待機できる時間を制限し、connIdleは接続のアイドル時間を制限します。uplinkOnlyとdownlinkOnlyは、一方向のデータ転送段階で待機できる時間に関係します。各項目の役割は、コアのドキュメントと実際の接続状態に照らして理解してください。ブラウザーの「ページ読み込みタイムアウト」と単純に同一視することはできません。
ハンドシェイク、アイドル状態、半閉鎖
ハンドシェイクの失敗は、回線が遅い場合だけでなく、設定の不一致でも発生します。認証情報やセキュリティ設定が間違っている場合、ハンドシェイクの待ち時間を延ばしても失敗が遅くなるだけです。接続確立に実際に時間がかかるという証拠がある場合に限り、少しずつ調整する意味があります。確認時は、接続開始、最初のデータ受信、エラー発生の時刻を比較してください。また、ログがどの階層のハンドシェイクを示しているのかを確認し、アプリのセッション、プロキシプロトコル、下位のトランスポートのハンドシェイクを混同しないようにしましょう。
アイドル状態の接続と速度の遅い接続は別のものです。長時間接続するアプリは、一定時間データを送受信しなくてもセッションを維持したい場合があります。ハートビートで接続を保つアプリもあります。ポリシーのタイマーとアプリのハートビート間隔が合わないと、一定の間隔で切断されることがあります。切断までの間隔、実際のデータ通信の有無、リモート側や中間機器が先に接続を閉じていないかを確認してください。ローカルのアイドル時間を延ばしても、リモート側が独自の条件でセッションを終了するのは防げません。無線ネットワークの切り替えによる接続状態の喪失も解決できません。
半閉鎖とは、片側が送信を終了しても、もう一方からまだデータが届く可能性がある状態です。この段階で応答を返すプロトコルもあり、一方向の待ち時間が短すぎると未完了のデータが途切れることがあります。一方、すべての値を長くすると、役目を終えた接続がリソースを使い続けます。具体的なアプリで問題を再現し、応答が完全に届くか、どちらが先に接続を閉じるかを確認してから、関連する項目を調整しましょう。1つのページを表示できたからといって、長時間接続や一方向通信のすべてのケースを確認できたとは限りません。
統計とバッファーは速度を変えるスイッチではない
ポリシーには、ユーザーまたはシステムのトラフィック統計に関する項目が含まれる場合があります。ただし、統計を有効にすることは記録に必要な条件の1つにすぎません。クライアント画面に情報を表示するには、統計モジュールやAPI、クライアント側の読み取り方法も関係します。統計のスイッチだけをコピーしても、画面にデータが自動で表示されるわけではありません。同様に、バッファーの設定はメモリ使用量やデータ処理に影響しますが、リモート側の帯域幅、回線の混雑、サーバーの制限を超えることはできません。バッファーを大きくしても、すべてのリクエストが速くなるわけではありません。
GUIクライアントでは、明確で再現可能な要件がない限り、生成された基本ポリシーを維持してください。クライアントは現在のコアや機能に合わせてデフォルト値を設定していることがあります。ポリシー全体をカスタム設定で置き換えると、元の項目が意図せず失われる可能性があります。1つの値だけ変更する場合は、クライアントが項目単位で統合するのか、セクション全体を置き換えるのか確認してください。変更前後の設定を比較し、特に変更していないレベルやシステムポリシーが残っているかを確認しましょう。タイムアウトだけを変えたつもりで、別の設定まで失うことを防げます。
ポリシーを調整する際は、同じアプリで同じ操作を行い、たとえば「アイドル状態の後も送信を続けられるか」「一方向の応答が最後まで届くか」など、確認できる判定基準を決めてください。安定して再現できない場合は、元の値を維持して証拠を集めましょう。ハンドシェイク、アイドル時間、バッファーを同時に変更しないでください。メモリに制約のある環境では、同時接続数とログ量も確認し、リソース不足を回線の問題と取り違えないようにします。ポリシーの目的は、実際の使い方に合わせて接続の制限を設定することであり、数値を大きくすること自体ではありません。
完全な設定例と段階的な検証
まずローカルinboundとdirect outboundだけの設定で構造を確認し、その後に実際のノードや複雑なルーティングを追加してください。こうすれば、リモート認証、トランスポート条件、サブスクリプションによる設定生成を、ひとまず検証の範囲外にできます。以下は、ローカルのSOCKSリクエストを受け付け、指定したテスト用ドメインは拒否し、それ以外はdirectに送る完全な基本設定です。リモートプロキシの経路を確立するものでも、アプリのシステムプロキシを自動設定するものでもありません。inbound、ルール、outboundの参照関係を確認するための例です。
実行できるローカル検証用設定
{
"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": {
"domainStrategy": "AsIs"
}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": ["full:blocked.example"],
"outboundTag": "block"
},
{
"type": "field",
"inboundTag": ["local-socks"],
"outboundTag": "direct"
}
]
},
"dns": {
"servers": ["localhost"]
},
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5
}
}
}
}
実行前に、同じ待ち受けポートを使っている他のプロセスを停止してください。クライアントのカスタム設定欄を使う場合は、完全な設定を読み込む欄か、特定のセクションだけを受け付ける欄かを確認してください。両者では必要な構造が異なります。保存後は、まずコアが起動するか確認し、続いて待ち受け状態を確認します。この例でスニッフィングを有効にしていないのは、ルールを検証する際に、アプリから渡された接続先名をそのまま使うためです。dnsには基本構造を残していますが、direct outboundでAsIsを使う場合、リクエストが必ず内蔵DNSを通ると判断することはできません。
inbound、照合、outboundの順にテストする
最初に、アプリが本機のポートに接続できることを確認します。すぐに接続拒否になる場合は、プロセスが起動しているか、アドレスファミリーが一致しているか、ポートが変更されていないかを調べてください。次に、アプリが先に名前解決してIPに置き換えるのを避けるため、ドメインを明示的に渡します。最後に、通常の接続先と拒否対象の動作を比較します。以下のコマンドは、対応するコマンドラインツールが用意されている環境向けです。実行中のローカルinboundにのみリクエストを送信します。ターミナルでツール名が別のコマンドのエイリアスになっている場合は、実際の実行ファイル名を使ってください。
curl --proxy socks5h://127.0.0.1:10808 --connect-timeout 10 --max-time 20 -I https://v2raygpu.com/
curl --proxy socks5h://127.0.0.1:10808 --connect-timeout 10 --max-time 20 -I https://blocked.example/
socks5hを使うと、ドメイン名をプロキシinboundに渡せるため、完全なドメインルールをテストできます。最初のリクエストが成功するかは、本機のネットワークと接続先サイトが利用可能かどうかにも左右されます。2つ目のリクエストは拒否されるはずですが、例のドメインは通常の業務用接続先ではないため、失敗したことだけでルール適用を証明することはできません。リクエストの接続先とoutboundの記録を確認するか、管理された環境でアクセス可能な接続先にテストルールを一時的に置き換えてください。テスト後は元に戻します。タイムアウトしただけで、拒否ルールが正常に働いたと判断しないでください。
基本経路を確認した後、クライアントに既存の実ノード情報を読み込み、実際に使うoutboundへ追加します。機能は一度に1つずつ追加してください。まずリモートoutboundを確認し、次にドメインルール、最後に名前解決条件を追加します。読み込み後すぐに失敗した場合は、追加した部分と基本設定を順に比較してください。完全な検証用設定をクライアントの自動生成フローにそのまま上書きしながら、元のノードやルーティングが残っていると想定しないでください。設定の生成元が変わった場合は、検証の起点も改めて確認する必要があります。
障害の記録と切り戻しの手順
障害記録には、使用中のクライアント、実際のコア、設定の生成元、inboundアドレス、テスト対象、想定するoutboundを明記してください。認証情報は削除しても、タグ間の関係は残しましょう。そうしないと、ルーティングの参照が有効か判断できません。特定のアプリだけで失敗する場合は、成功するアプリとinboundの使用方法、ドメインの渡し方、接続の再利用方法を比較します。すべてのアプリで失敗する場合は、クライアントを最初から再インストールするのではなく、起動、待ち受け、名前解決、リモート接続の順に調べてください。
切り戻しは、最後に行った変更から逆順に進めます。まず追加したルールや項目を取り消し、再起動して再テストします。元に戻らない場合は、動作確認済みの設定の生成元に切り替えてください。サブスクリプションの更新で変わった場合は、ノード情報の変更とローカルテンプレートの変更を区別します。古いファイルと新しい画面の設定を混在させないでください。プラットフォームごとのインストール方法はダウンロードページ、基本の接続手順はクイックスタートガイド、用語の意味は用語集をご覧ください。
最後に、目的が実際に達成できたかを確認してください。構文が正しいこと、待ち受けが正常なこと、リクエストが届くこと、ルールどおりのoutboundが選ばれること、名前解決の経路が明確なこと、接続が想定した制限内で終了することを確認します。不明な点があれば、該当する観察ポイントを引き続き確認してください。このページの設定片は各モジュールの説明用です。全体をそのまま使えるのは、完全な設定例として明記されたものだけです。セクションをコピーする場合は正しい階層に統合し、他の必須モジュールも残してください。安定した設定は項目を増やすことではなく、依存関係を繰り返し検証することで実現します。