Xrayのルーティングルールを編集する方向けに、ドメインとIPアドレスの違いから、ルールの構文、フィールドの組み合わせ、名前解決の方針まで解説します。routingの設定例を確認しながら、接続が直結・プロキシ・ブロックのどれになるか、ルール変更後にどのログを確認すべきかを把握できます。例には予約済みのテスト用ドメインを使用しており、実在サービスの認証情報は扱いません。
ルーティングの入力情報と出力タグ
Xrayのルーティングは出力先を選択する機能であり、アプリの代わりにリクエストを作成するものではありません。アプリからの接続はまずinboundsに入り、ルーティングが宛先アドレスやポート、ネットワーク種別などを確認して、該当するoutboundsに接続を引き渡します。v2rayNはクライアントの操作画面です。この記事のコードはXrayコアのJSON設定例であり、サブスクリプションリンクとしてはインポートできません。
各ルールのoutboundTagは、実際に定義されている出力先のtagと一致させる必要があります。ここで使うdirect、proxy、blockは任意に付けたタグ名であり、名前を書くだけで機能が自動的に追加されるわけではありません。通常はそれぞれ、直結用、設定済みプロキシ用、ブロック用の出力先に割り当てます。
- ドメインがある場合:SOCKSまたはHTTPプロキシにドメイン名を渡すと、通常はルーティングでそのままドメインルールを判定できます。
- IPアドレスのみの場合:アプリ側で名前解決してからIPアドレスをプロキシに渡すと、照合できるドメイン名がないため、ドメインルールは直接適用できません。
- スニッフィングを有効にした場合:コアは対応するハンドシェイク情報からドメイン名を取得できることがあります。宛先を置き換えるのか、ルーティング判定だけに使うのかは、スニッフィングの設定によって異なります。
- どのルールにも一致しない場合:通常のXray設定では、最初の出力先が使われます。この暗黙の動作を、固定の「デフォルトプロキシ」と誤解しないでください。
domain・full・regexpの違い
ドメインの照合対象は、ルールのdomain配列に指定します。domainはフィールド名であると同時に、配列要素の照合プレフィックスとしても使われます。フィールド名は「ドメインを調べる」ことを示し、プレフィックスは「どのように比較するか」を指定します。以下のexample.testは構文説明用の予約済みテストドメインで、アクセス可能なサービスを示すものではありません。
| 配列要素 | 照合方法 | 補足 |
|---|---|---|
full:api.example.test | 完全一致 | このホスト名だけに一致し、サブドメインは含みません。 |
domain:example.test | ドメインとサブドメインに一致 | ルートドメインとapi.example.testに一致し、notexample.testには一致しません。 |
example | 部分文字列で照合 | 部分文字列として照合するため、通常はドメインのサフィックス照合より広い範囲に一致します。 |
regexp:^api[0-9]+\\.example\\.test$ | 正規表現で照合 | JSON文字列として記述するため、バックスラッシュはエスケープが必要です。 |
geosite:cn | ドメインセットで照合 | ローカルのドメインデータファイルから指定したセットを読み込みます。 |
特定のホストだけをプロキシ経由にする場合は、full:を使います。ドメインツリー全体を対象にする場合は、domain:を使います。通常のドメイン項目に*.example.testを汎用ワイルドカードとして指定することはできません。ワイルドカードのように動作させるには、ドメインのサフィックス照合か、明示的な正規表現を使います。
{
"type": "field",
"domain": [
"full:api.example.test",
"regexp:^edge[0-9]+\\.example\\.test$"
],
"outboundTag": "proxy"
}
上の例では、2つの配列要素は「OR」の関係にあり、どちらか一方に一致すれば条件を満たします。正規表現でピリオドを文字どおりに扱うには、JSONでは\\.と記述します。先頭と末尾の^および$で照合範囲を限定し、ホスト名の前後に余分な文字がある場合の誤一致を防ぎます。
ip・geoip・geositeデータセット
ipフィールドでは宛先IPアドレスを判定します。単一アドレス、CIDR形式のネットワーク、またはgeoip:セットを指定できます。ドメインセットはdomain、IPセットはipに指定します。geositeとgeoipはオンライン検索サービスではなく、別のフィールドとして使うこともできません。
{
"type": "field",
"ip": [
"192.168.0.0/16",
"10.0.0.0/8",
"fc00::/7",
"geoip:private"
],
"outboundTag": "direct"
}
この例では、プライベートネットワークをいくつか明示し、さらにgeoip:privateセットを使って対象を補っています。一部の範囲は重複していますが、構文を比較するためのものです。実際の設定では、使用するデータファイルの内容に合わせて簡略化できます。192.168.0.0/16はネットワーク範囲を表し、192.168.0.0という単一アドレスだけを照合する指定ではありません。
geosite.datにはドメインセット、geoip.datにはIPセットが収録されています。現在のコアがリソースの参照先からファイルを読み込める必要があります。geosite:cnはドメインセット、geoip:cnはアドレスセットを表します。両者の判定結果が一致するとは限りません。- セット名、属性による絞り込み、対象範囲は、実際に使用するデータファイルによって異なります。分類名だけを見て、特定のサイトが必ず含まれていると判断しないでください。
- ルール変更後にコアが起動しない場合は、まずログにファイル読み込み失敗やセット未登録のエラーがないか確認してください。すぐに接続先ノードを変更する必要はありません。
ルールの優先順位とフィールドの組み合わせ
routing.rulesは配列の順に判定され、条件に一致した最初のルールで出力先が決まります。ルールが具体的だからといって、コアが自動的に優先順位を上げるわけではありません。先にある広範囲のルールで判定が終了すると、後続の例外ルールは適用されません。
{
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": ["full:api.example.test"],
"outboundTag": "proxy"
},
{
"type": "field",
"domain": ["domain:example.test"],
"outboundTag": "direct"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
この設定例では、api.example.testはプロキシ経由、www.example.testは直結となり、それ以外のTCP・UDP接続は最後のルールでプロキシに送られます。2番目のルールを1番目より前に置くと、APIのドメインも先にドメインツリーのルールに一致し、直結になります。
| 設定箇所 | 論理関係 | 実際の意味 |
|---|---|---|
同じdomain配列内の複数項目 | OR | いずれか1項目に一致すれば、そのフィールドの条件を満たします。 |
同じip配列内の複数ネットワーク | OR | いずれかのネットワーク範囲にアドレスが含まれれば、そのフィールドの条件を満たします。 |
同一ルール内のdomainとport | AND | ドメインとポートの両方が一致する必要があります。 |
同一ルール内のdomainとip | AND | ドメインとIPのどちらか一方を選ぶ条件ではありません。IP条件の判定には、宛先アドレス情報と名前解決の方針も関係します。 |
結論:例外ルールはセットルールより前に配置
特定ホスト向けの例外処理を最初に記述し、次にドメインツリーや分類セット、最後にフォールバックルールを記述します。「ドメインまたはIPのどちらかに一致したら直結」させたい場合は、両方のフィールドを1つのルールに入れず、2つのルールに分けてください。
domainStrategyがIP判定に与える影響
routing.domainStrategyは、ルーティングの段階でドメインにIP情報を補うかどうかを決めます。ルールの順序を変更する設定でも、DNSサーバーのアドレスを指定するフィールドでもありません。設定にdnsセクションがあっても、それだけでルーティングが各ドメインのIP判定を自動実行することはありません。
| ポリシー | ルーティング時の動作 | 注意点 |
|---|---|---|
AsIs | ルーティング判定のために、ドメインの名前解決を積極的には行いません。 | 宛先がもともとIPアドレスの場合は、IPルールで引き続き判定できます。 |
IPIfNonMatch | ドメインがルールに一致しなかった場合に、名前解決を試みてIP条件を含むルールを判定します。 | 最初の判定で広範囲のフォールバックルールに一致すると、不一致を条件とする名前解決は行われません。 |
IPOnDemand | IP情報が必要なルールに到達すると、ドメインの名前解決を試みてから判定を続けます。 | 前方にあるIPルールによって名前解決が必要になり、後続のドメインルールが実行されるかどうかに影響することがあります。 |
よくある誤解は、IPIfNonMatchを選び、先頭にgeoip:cn、末尾にTCP・UDPすべてを対象とするプロキシルールを置けば、すべてのドメインを先にIPの地域で振り分けられるというものです。ドメイン宛ての接続では、1回目の判定で末尾のプロキシルールに一致することがあります。一致した時点で処理が終わるため、「不一致後に名前解決」する機会はありません。
- 主にドメインセットで振り分ける場合:明確なドメインルールを先に設定し、IPアドレスだけのリクエストをどう扱うかも確認します。
- ドメインに一致するルールがない場合だけIPで判定する場合:
IPIfNonMatchを使い、先に一致してしまう汎用ルールがないか確認します。 - 前方にあるIP条件もドメイン宛てリクエストの判定に使いたい場合:
IPOnDemandを検討し、DNSの応答とルール順が想定どおりか確認します。
ルーティング時に取得したIPアドレスが、実際の接続で使われるアドレスと同じとは限りません。出力先がドメイン名を引き継ぐか、どこで名前解決するかは、使用するプロトコルや出力先の設定にも左右されます。トラブルシューティングでは、「なぜこのタグが選ばれたか」と「接続が最終的にどこへ向かったか」を分けて確認してください。
ポートを使った最小限の検証
最初から数十件の分類ルールを読み込んで、ウェブページが開くか確認するのは避けてください。まずは明確な受信ポート、ポートを限定したドメインルール、判別しやすい出力タグを使って、ルールの適用経路を検証します。以下の3つの数値はこのテスト設定で指定する値であり、すべてのクライアントバージョンの既定値ではありません。
{
"type": "field",
"domain": ["full:api.example.test"],
"port": "443",
"network": "tcp",
"outboundTag": "proxy"
}
このルールに一致するのは、宛先ドメイン、宛先ポート443、TCPのすべてが条件を満たす接続だけです。同じホストのポート80やUDP 443には一致しません。portが確認するのは宛先ポートであり、ローカルSOCKSの待ち受けポート10808ではありません。ブラウザがUDPベースの通信を使う場合、このTCPルールから接続結果を推測することはできません。
- 現在正常に動作している設定をバックアップし、コアが新しいルーティング設定を読み込んでいることを確認します。古いプロセスが動作したままになっていないかも確認してください。
- ローカルの受信ポートが実際に
127.0.0.1:10808で待ち受けていることを確認します。別のポートを使う場合は、実際の設定値を基準にしてください。 - テスト環境で、予約済みドメインを解決するか、スニッフィングに渡すための情報を用意します。テスト用サービスを用意していない場合は、コアがルールを読み込めることだけを確認し、アクセス失敗をルーティング失敗と判断しないでください。
- DNSの動作を比較する場合は、ログレベルを一時的に上げてリクエストを1回再実行し、受信した宛先、DNSクエリ、出力タグを確認します。テスト後はログレベルを元に戻してください。
- 変更する項目は一度に1つに絞ります。まずルール順を変更し、次に
domainStrategyを変更して、最後にデータセットを追加します。各段階の結果を記録しておけば、複数の変更が互いに影響して原因が分からなくなるのを防げます。
v2rayNのルーティング設定に関するトラブルシューティング
v2rayNでルーティングを変更したら、現在選択しているルーティング設定とコアの種類も確認してください。この記事で説明するのはXrayのフィールドの意味です。画面上のルーティング設定はクライアントが変換して生成設定に書き込む場合があるため、JSON全体をドメイン項目専用の入力欄に貼り付けないでください。
fullルールを追加したのに、出力先が変わらないのはなぜですか?
まず、そのルールを広範囲のドメインセットやフォールバックルールより前に移動し、設定を再読み込みします。ログに記録された宛先がまだドメイン名か確認してください。IPアドレスしか表示されない場合は、アプリが自分で名前解決しているか、受信側のスニッフィングで利用可能なドメイン名を取得できているかを確認します。
geositeルールを保存した後、コアが起動しないのはなぜですか?
コアの起動ログを確認し、データファイルを読み込めるか、セット名が存在するか、ルールを正しいフィールドに指定しているかをそれぞれ確認します。まずfull:ルール1件でルーティングを検証してからセットルールを戻すと、構文の問題とデータファイルの問題を切り分けられます。
IPIfNonMatchを設定したのに、想定したDNSクエリが確認できないのはなぜですか?
最初の判定ですでにドメインルール、またはnetwork: tcp,udpのフォールバックに一致していないか確認してください。一致している場合、「不一致」という発動条件を満たしません。IP条件を先に判定させたい場合は、IPOnDemandを検討してください。IPルールの位置を変えるだけで結果を推測しないでください。
同じルールにdomainとipを指定したら、一致件数が減ったのはなぜですか?
この2つのフィールドは、両方の条件を満たす必要があります。ドメインセットまたはアドレスセットのどちらかに一致すれば直結させたい場合は、ルールを2つに分け、同じoutboundTagを指定してください。その後、ドメイン宛てとIPアドレス宛てをそれぞれテストします。
生成された設定を編集しても、クライアントを再起動すると元に戻る場合はどうすればよいですか?
自動生成された設定は、ノードの切り替えやコアの再起動時に再作成されることがあります。クライアントに用意されたルーティング設定の画面からルールを保存するか、対応しているカスタム設定方法を利用してください。再起動後は、コアが実際に読み込んだ内容をもう一度確認します。エディターで保存できたことだけを、設定が適用された根拠にしないでください。
最終確認は、入力された宛先、ルール条件を満たすか、最初に一致するルール、タグが存在するか、出力先に接続できるかの順に行います。ウェブページが開いたことだけでは、ルーティングが正しいとは証明できません。最小限のテストルールと対応するログを残すほうが、複数のルーティングモードを同時に切り替えるより原因を特定しやすくなります。