config.jsonの構造を段階別に解説:inbounds・outbounds・routingの役割
最小構成の設定を分解し、inboundsのローカル待受、outboundsのリモート接続、routingの出口振り分け、3つをつなぐtagの役割を解説します。
設定がv2rayNのサブスクリプション機能で生成された場合でも、管理者が手作業で整えた場合でも、基本構造は3つの問いに整理できます。通信はどこから入り、どこから出て、どの条件で出口が決まるのか。これを理解すれば、「ポートには接続できるのにWebページが開かない」「直結対象のドメインまでプロキシを経由する」「ブロックルールが適用されない」といった障害も効率よく切り分けられます。
この記事は、サブスクリプションをインポートでき、さらに低レベルの設定を理解したい方に適しています。読み終えると、inbounds、outbounds、routing、tagの役割を見分け、SOCKS入入口、VMessプロキシ出口、直結出口、ブロック出口を含む設定を読み解けます。また、ログの順序に沿って、ルール未適用、ポート競合、出口参照エラーを特定できます。
まず通信経路全体を把握する:入入口・マッチング・出口
アプリケーションが直接「routingに入る」わけではありません。ブラウザーなどのプログラムがまずローカルの待受ポートへリクエストを送り、inboundsが接続を受けてコア処理用の宛先情報を生成します。routingは宛先ドメイン、宛先IP、ポート、ネットワーク種別、入入口タグを読み取り、条件に一致するとルールのoutboundTagが特定のoutbounds項目を指します。どのルールにも一致しない場合は、通常outbounds配列の先頭項目がデフォルト出口になるため、配列の順序も設定ロジックの一部です。
config.jsonは、互いに無関係なパラメータの集まりではなく、有向接続グラフとして捉えると分かりやすくなります。inboundsのtagは通信元を示し、outboundsのtagは出口に名前を付け、routing.rulesがそれらの名前を参照して接続を完成させます。tag自体がデータを転送したり、自動でプロキシチェーンを構築したりするわけではありません。コア内部で使う、安定した大文字・小文字を区別する参照キーです。
inbounds:ローカルでの通信受付方法を定義
inboundsは入入口の配列で、各項目が1つの待受入口を表します。デスクトップでは、SOCKS、HTTP、またはクライアントが管理する透過プロキシ入口がよく使われます。最も基本的なSOCKS入入口では、listen、port、protocol、settingsを明示します。待受アドレスを127.0.0.1にするとローカルアプリだけがアクセスできます。0.0.0.0に変更するとすべてのネットワークインターフェースで待ち受けるため、LAN内の端末からアクセスされる可能性があります。両者を同じものとして扱ってはいけません。
{
"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:この入口をsocks-inと命名します。後続のルールではinboundTagを使い、この入口からのリクエストだけを処理できます。listen:バインドするアドレスを指定します。127.0.0.1はローカルループバックアドレスで、現在のPCだけで使うクライアント設定に適しています。port:待受ポートを指定します。例では10808を使用しているため、ブラウザーやアプリのSOCKS5プロキシポートにも10808を入力します。protocol:アプリケーションがローカルのコアと通信する方法を宣言します。ここではSOCKSであり、リモートノードもSOCKSを使うという意味ではありません。settings.udp:SOCKS入入口でUDPリクエストを受け付けます。これは入入口側の機能を有効にするだけで、実際に利用できるかはリモートプロトコル、トランスポート方式、対象アプリにも左右されます。sniffing:HTTPリクエストやTLSハンドシェイクから宛先ドメインを復元し、ドメインベースの振り分けルールを適用できるようにします。
ポート競合は、コアが待受ソケットを作成するときに発生します。10808が別のプロセスに使用されている場合、inboundsはまだ通信を受け付けておらず、その後のroutingやoutboundsも実行されません。v2rayNでは「設定」→「パラメータ設定」からローカルSOCKSポートを変更できます。10818に変更したら、ブラウザーのプロキシ、システムプロキシ、その他のアプリでもポートを10818に合わせてください。
outbounds:プロキシ・直結・ブロックの出口を定義
outboundsは出口の配列です。各出口には一意のtagが必要で、protocolが離脱通信の処理方法を決めます。リモートプロキシ出口にはサーバーアドレス、ポート、ユーザー識別子、トランスポートパラメータが含まれます。直結出口にはfreedom、接続を能動的に終了する出口にはblackholeを使います。3種類を同時に用意し、routingで実際の出口を選択できます。
proxy:VMessプロキシ出口
- プロトコル
- VMess
- サーバーポート
- 443
- トランスポート
- WebSocket
- トランスポートのセキュリティ
- TLS
ノードパラメータは通常サブスクリプションからインポートされます。address、id、パス、トランスポートのセキュリティ設定はサーバー側と一致していなければなりません。
direct:直接接続
- プロトコル
- freedom
- リモートノード
- 不要
- 代表的な用途
- LANと指定ドメイン
- 参照タグ
- direct
直結では、proxy出口を経由せず、ローカルネットワークから対象へ直接アクセスします。
block:接続を終了
- プロトコル
- blackhole
- リモート接続
- 確立しない
- 代表的な用途
- 広告ドメインルール
- 参照タグ
- block
一致するとコアがリクエストを終了します。明確にブロックしたい対象群に適しています。
デフォルト出口
- 決定方法
- 配列の先頭項目
- タグの例
- proxy
- 適用条件
- ルールに一致しない場合
- 確認ポイント
- 並び順
分類されていない通信をプロキシ経由にしたい場合は、outboundsの先頭にproxyを置きます。
{
"outbounds": [
{
"tag": "proxy",
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "node.example.net",
"port": 443,
"users": [
{
"id": "11111111-1111-4111-8111-111111111111",
"security": "auto"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"wsSettings": {
"path": "/connection"
}
}
},
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
]
}
上記のドメイン、ユーザー識別子、パスは構造を示すためのもので、サブスクリプションの実ノードパラメータの代わりにはなりません。VMessのsettingsはユーザーとサーバーを、streamSettingsはトランスポート層を定義します。アドレスとポートが正しくても、WebSocketパス、TLS設定、ユーザー識別子のいずれかが一致しなければ接続は失敗します。
結論:まずデフォルト出口を確認し、次に複雑なルールを調べる
routingを一時的に無効にしてもアクセスできない場合は、proxy出口のパラメータまたはネットワーク接続に問題がある可能性が高いです。無効化すると正常に戻るなら、ルールの順序、マッチ条件、outboundTagの参照を確認します。
routing:条件に応じてリクエストを指定出口へ渡す
routingは待受を作成せず、リモートノードの認証情報も保持しません。役割はリクエストの特徴を読み取り、出口タグを返すことです。rules配列は上から順に照合され、通常は最初に有効なルールへ一致した時点で検索を終了します。そのため、範囲が狭く優先度の高いルールを前に置き、広範囲のフォールバックルールを後ろに置きます。
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"domain": [
"geosite:category-ads-all"
],
"outboundTag": "block"
},
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "direct"
},
{
"type": "field",
"inboundTag": [
"socks-in"
],
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
3つのルールが解決する問題
- 1つ目はGeoSiteの分類を読み取り、広告ドメインに一致したら
blockへ渡します。このルールを使うには、ローカルのGeoSiteデータをコアが正常に読み取れる必要があります。 - 2つ目はプライベートIP範囲に一致した通信を
directへ渡し、LANアドレスがリモートプロキシ出口へ回るのを防ぎます。 - 3つ目は送信元を
socks-inに限定し、残りのTCP・UDPリクエストをproxyへ渡します。範囲が広いため、具体的なルールの後ろに置きます。
| フィールド | 確認対象 | よくある値 | 起こりやすい問題 |
|---|---|---|---|
domain |
宛先ドメイン | domain:、full:、geosite: |
分類データが古く、ドメインが想定した集合に含まれていない |
ip |
宛先IP | CIDR、geoip:private |
ドメインがIPに解決されず、ルールの照合対象にならない |
port |
宛先ポート | 53、80,443、ポート範囲 |
ローカル待受ポートを宛先ポートと取り違えている |
network |
トランスポート種別 | tcp、udp、tcp,udp |
tcpだけを指定し、UDPリクエストを取りこぼしている |
inboundTag |
通信元 | socks-in |
参照名がinboundsのtagと一致していない |
outboundTag |
宛先出口 | proxy、direct、block |
outboundsに存在しないタグを参照している |
domainStrategyは、ドメインルールとIPルールの間で使う名前解決方針を制御します。AsIsは元のドメインを優先して照合し、すべてのIPルールのためにドメインを積極的に解決することはありません。IPIfNonMatchはまずドメインルールを試し、一致しなければIPを解決してIPルールを続けて確認します。IPOnDemandは照合中に、より積極的に名前解決を行います。一般的な振り分け設定ではIPIfNonMatchから始めると、ドメイン分類を維持しつつ、プライベートIPなどのルールも適用しやすくなります。
結論:ルールは数より順序が重要
広告ブロックとLAN直結を先に置き、その後にすべてのTCP・UDPを対象とするプロキシルールを置きます。広範囲のルールが先にあると、後続のより具体的なdirectやblockルールが実行されない可能性があります。
tagで3段階の設定を1本の経路につなぐ方法
tagで重要なのは、「定義されていること、綴りが一致していること、用途が明確であること」です。入入口タグがsocks-inで、3つの出口タグがproxy、direct、blockだとすると、routingが参照できるのは定義済みのこの4つの名前だけです。outboundTagをProxyやproxy-mainと書いても、proxyへ自動的に関連付けられることはありません。
inbounds[0].tag = "socks-in"
routing.rules[2].inboundTag = ["socks-in"]
outbounds[0].tag = "proxy"
routing.rules[2].outboundTag = "proxy"
outbounds[1].tag = "direct"
routing.rules[1].outboundTag = "direct"
outbounds[2].tag = "block"
routing.rules[0].outboundTag = "block"
リクエストが実際に通る順番で確認する
- アプリのプロキシ種別がSOCKS5で、アドレスが127.0.0.1、ポートがinbounds.portと同じであることを確認します。例ではポート10808です。
- コアのログに「アドレスはすでに使用されています」や設定解析失敗のメッセージがないことを確認します。待受に失敗している間は、リモートプロトコルを調べても意味がありません。
- リクエストが想定した入入口から来ていることを確認します。ルールが
inboundTagを限定している場合、別のHTTP入入口から入ったリクエストはそのルールに一致しません。 - routing.rulesの並び順に従って最初に一致する項目を探し、その
outboundTagを記録します。 - outboundsから完全に同名のtagを探し、対応するprotocol、サーバーアドレス、ポート、streamSettingsを確認します。
- どのルールにも一致しない場合は、outboundsの先頭項目が本当に使用予定のデフォルト出口か確認します。
送信元ごとにポリシーを分ける場合は、複数の入入口を設定できます。例えば10808のsocks-inはデフォルトでproxyへ、10818の別のSOCKS入入口はデフォルトでdirectへ送ります。この場合、2つの入口には異なるportと異なるtagを設定し、それぞれにinboundTag付きのルールを記述します。入入口だけをコピーして同じポートを残すと、起動時に待受ポートの競合が発生します。
設定変更時の安全な手順とよくある問題
v2rayNは、現在のノード、ルーティング設定、コアのオプションに基づいて実行用設定を生成します。生成ファイルを直接編集しても現在のプロセス中だけ有効で、ノード切り替え、サブスクリプション更新、クライアント再起動後に再生成されることがあります。長期的に保持したいルーティングは、クライアントのルーティング設定で管理するのが基本です。ローカルポートを変更するときは「設定」→「パラメータ設定」を使い、そのポートを呼び出すすべてのアプリにも反映してください。
- 変更前に現在動作している設定のコピーを保存し、毎回1つのフィールドグループだけを調整します。
- JSONを編集したら、カンマ、引用符、配列、オブジェクトの閉じ方を確認してからコアを起動します。
- outboundsの順序を変更するときは、先頭項目のtagを記録します。ルールに一致しない場合のデフォルト出口に関わるためです。
- routing.rulesを変更するときは、具体的なルールから広範囲のルールへ並べ、想定する出口を1件ずつ記録します。
- サブスクリプション更新後は、プロトコル、トランスポート、TLS、サーバーポート、ユーザー識別子が正しくインポートされているか再確認します。
- ルーティングの分類結果がおかしいときは、GeoIPとGeoSiteのデータを現在のコアが読み取れるか確認します。
ポート10808には接続できるのに、なぜWebページが開かないのですか?
ローカルポートに接続できるのは、inboundsが待ち受けていることを示すだけです。ログレベルを一時的にinfoにし、リクエストがproxyに一致しているか、VMess出口のサーバーアドレス、443ポート、ユーザー識別子、WebSocketパス、TLS設定がサブスクリプションと一致しているか確認します。
directルールを書いたのに、なぜ宛先ドメインがプロキシを経由するのですか?
まず、そのルールが広範囲のproxyルールより前にあるか確認し、次にdomainStrategyとマッチング種別を確認します。IP分類ルールを使う場合は、方針をIPIfNonMatchに設定し、名前解決結果が実際に対象IP範囲へ入っているか確認します。
outboundTagは自由に命名できますか?
名前は自由に決められますが、outbounds内の項目のtagと完全に一致させ、重複は避ける必要があります。proxy、direct、blockのように役割が明確な短い名前がおすすめです。名前を変更するときは、すべてのrouting参照も同時に検索してください。
サブスクリプション更新後に手動ルーティングが消えるのはなぜですか?
実行用設定がv2rayNによって再生成されている可能性があります。長期的に使うルールはクライアントのルーティング設定に保存し、一時生成されたconfig.jsonだけを編集しないでください。更新後は、ルールの順序と出口タグが維持されているか再確認します。
VMessとVLESSの設定構造はまったく同じですか?
最上位ではinbounds、outbounds、routingを引き続き使いますが、出口のprotocol、settingsのユーザーフィールド、streamSettingsの組み合わせは異なります。protocol文字列だけを置き換えず、サブスクリプションからインポートしたノードパラメータ全体を基準にしてください。
config.jsonを理解するうえで重要なのは、すべてのフィールドを暗記することではありません。決まった切り分け手順を守ることです。入入口が待ち受けているか、リクエストにどの宛先情報が付いているか、どのルールが先に一致したか、outboundTagがどの出口を指すか、その出口で接続を完了できるかを順に確認します。ポート、プロトコル、ルーティングを同時に変更するより、この経路に沿って段階的に検証するほうが原因を特定しやすくなります。