V2Ray 配置文件结构详解:inbounds、outbounds 与 routing 逐段拆读

从一份完整的 config.json 出发,逐段说明入站、出站与路由规则各字段的作用和相互引用关系,读懂客户端生成的配置。

本文速览

本文适合已经完成客户端导入、希望读懂内核运行配置的用户。先用一份只含本地入站、直连和拒绝出站的完整 JSON 建立结构,再追踪 tag 引用、路由顺序与 DNS 的关系。示例不含远端代理服务器;读完可判断请求从哪里进入、由哪条规则处理,以及修改后应检查哪些字段。

一、先区分客户端设置与内核配置

v2rayN、v2rayNG、v2flyNG 是客户端,不等于内核本身。客户端负责管理订阅、节点和操作界面,再把相关设置转换为内核可读的配置。v2rayNG 使用 Xray 内核,v2flyNG 使用 v2fly 内核;v2rayN 的运行配置则需要结合当前选择的内核判断。不同内核、不同版本的字段支持范围不能直接画等号。

本文采用 V2Ray 传统 JSON 配置结构中的常见字段。阅读实际配置时,先确认它是内核配置,还是客户端自己的设置文件。界面里保存的订阅分组、窗口选项、更新偏好,不一定出现在内核的 config.json 中;反过来,运行配置中的本地端口也可能由客户端自动生成。

应用代理请求 本地入站接收 路由规则匹配 指定出站处理 目标连接建立

这条链描述的是显式使用本地代理的应用。仅启动内核,不会自动让所有程序接入本地端口。系统代理、应用内代理与 TUN 的接入方式不同;本篇先固定为 SOCKS 或 HTTP 入站,避免把接入范围问题误判为路由错误。

二、一份完整的 config.json 示例

下面是用于拆读结构的完整配置。它监听两个回环地址端口,把 example.org 的精确域名请求拒绝,其余流量直连。example.org 是文档示例域名,不代表可用节点。配置中没有 VMess 或 VLESS 远端出站,因此它不会把流量转交给代理服务器。

10808
本例 SOCKS 入站端口
10809
本例 HTTP 入站端口
3 条
按顺序匹配的路由规则
{
  "log": {
    "loglevel": "warning"
  },
  "dns": {
    "servers": [
      "1.1.1.1"
    ]
  },
  "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": {}
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "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"
        ],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "inboundTag": [
          "socks-in",
          "http-in"
        ],
        "network": "tcp,udp",
        "outboundTag": "direct"
      }
    ]
  }
}

标准 JSON 中,键名和字符串必须使用双引号,数组与对象最后一项后面不能多写逗号。代码块没有注释,可作为独立阅读样本保存;是否能由具体内核加载,仍应使用该内核的配置检查命令确认。

三、inbounds:监听地址、端口与入口标签

inbounds 是数组,因为同一个内核进程可以接收多个入口。每个入口的 protocol 决定接收什么协议,settings 的内部字段也随协议变化。本例 SOCKS 入口的 udp: true 表示允许 SOCKS UDP 转发能力,不表示所有应用的 UDP 都会自动被接管。

SOCKS 入口

tag
socks-in
listen
127.0.0.1
port
10808
protocol
socks
auth
noauth

应用需明确连接该 SOCKS 入口;回环监听把接入范围限制在本机。

HTTP 入口

tag
http-in
listen
127.0.0.1
port
10809
protocol
http
settings
空对象

用于 HTTP 代理请求及 CONNECT 隧道,不等于在本地开放一个网站。

listen 与 port 共同确定监听位置。把 127.0.0.1 改为 0.0.0.0 会改变可接入范围,可能让其他设备访问该代理端口。本例又使用无认证 SOCKS,因此不应为方便连接就随意扩大监听范围;确有局域网需求时,应同时规划认证和防火墙。

tag 是配置内部引用的标识,不是服务器地址,也不会自动创建一个新端口。第三条路由规则中的 inboundTag 正是引用这里的两个字符串。把入口标签改名后,对应规则也必须同步修改,大小写和连字符都要一致。

四、outbounds:出站类型与默认去向

outbounds 定义连接离开内核时的处理方式。freedom 表示由本机直接连接目标,blackhole 用于拒绝流量。这里的 direct 与 block 只是人为命名的标签;真正决定出站行为的是 protocol,不是标签看起来像什么。

位置字段或值实际作用
第一项出站tag: direct供路由引用,并在本例中承担未匹配时的默认出站。
直连协议protocol: freedom直接建立到目标地址的连接。
第二项出站tag: block被精确域名拒绝规则引用。
拒绝协议protocol: blackhole不把请求转发到目标服务器。

在这里使用的常规路由机制中,没有规则匹配时会使用第一个出站。因此,调整 outbounds 数组顺序不是单纯整理显示顺序,而可能改变默认流向。本例又给两个已知入口写了末尾直连规则,让默认意图更容易被读出。

结论:改出站前先核对标签引用

先搜索所有 outboundTag,确认它们都能对应到现有出站的 tag;再检查第一项出站是否符合未匹配流量的预期。标签拼错不会自动退回到同名协议。

如果要接入远端节点,需要添加参数真实、协议受当前内核支持的代理出站,再把相应规则指向它。VMess 与 VLESS 的用户字段、传输层和安全设置不是同一套内容,不能只把 protocol 字符串改掉就认为转换完成。应以可信订阅导入后生成的配置为参照,逐项核对服务器、端口、用户标识与传输参数。

五、routing:规则顺序与匹配条件

routing.rules 是有顺序的规则数组。对一次请求,从前向后检查,遇到匹配规则便采用其指定的出站,不会继续把后面的规则全部执行一遍。把宽泛规则放在前面,可能让后面的精细规则一直没有机会命中。

  1. 先匹配精确域名。full:example.org 只匹配该完整域名,不包括它的子域名。只有请求携带或内核获得该域名时,这条域名规则才能参与对应匹配。
  2. 再匹配指定 IP 网段。本例列出回环与三个常见私有 IPv4 网段,匹配后走 direct。这不是完整的特殊地址清单,也没有覆盖 IPv6 私有或链路本地范围。
  3. 最后匹配两个本地入口。来自 socks-in 或 http-in,并且网络类型为 TCP 或 UDP 的请求,交给 direct。

一条规则里,inboundTag 数组中的两个入口是“任意一个符合即可”;同一规则中不同条件字段则通常需要同时成立。第三条规则因此不是“来自这两个入口,或者是 TCP”,而是“来自这两个入口之一,并且网络类型符合”。

domainStrategy: "AsIs" 表示路由匹配不为 IP 规则主动把域名解析成 IP。若应用交给内核的是域名,第二条私有地址规则不会仅凭“这个域名最终可能解析为私有地址”就命中;若交来的是 IP,则可直接做网段匹配。该选项控制路由阶段的处理,不等于整个连接过程中不发生 DNS 查询。

六、dns 与 log:不要混淆解析和分流

dns.servers 在示例中配置了 1.1.1.1,它属于内核内置 DNS 的服务器设置,不会自动替换操作系统或浏览器自己的解析配置。仅写入这个字段,也不能保证所有解析请求都会通过代理。哪些模块调用内置 DNS、解析连接走哪条路径,要结合具体内核与相关策略判断。

log.loglevel 设为 warning,适合先关注警告与错误。排查规则时可以短时改为 info;需要更细节时再考虑 debug。日志级别不是访问日志文件路径,也不保证所有版本都输出相同格式。分享日志前,处理订阅地址、用户标识、服务器地址和访问目标等敏感信息。

七、修改、验证与回退的操作顺序

客户端可能在切换节点、更新订阅或重新启动时重建运行配置。直接修改自动生成文件,下一次操作后内容可能被覆盖。先区分“查看生成结果”和“使用自定义配置”两种目的,再决定从客户端界面修改,还是使用其提供的自定义配置入口。

  1. 保存可回退副本。保留原配置,并记录当前内核类型、配置文件位置与监听端口,不要把订阅地址放进公开笔记。
  2. 每次只改一个逻辑点。例如只新增一条域名规则,先不同时更换 DNS、端口和远端节点,避免无法判断是哪项引起变化。
  3. 先做语法与配置检查。标准 JSON 检查能发现逗号和引号问题;内核检查才能进一步发现未知协议、字段类型及部分引用问题。
  4. 再启动并检查监听。如果提示地址占用,先确认 10808、10809 是否已有客户端使用,不要同时运行两份相同端口配置。
  5. 最后验证实际路径。先确认应用确实进入本地入口,再观察目标请求是否命中预期出站,失败时恢复上一份可用配置。

常见内核命令行检查写法如下,二者择一,不要连着对不同内核套用。可执行文件名称和调用方式需按本机系统调整;如果当前版本提示选项不识别,以它的 help 输出为准。检查通过只说明配置被接受,不证明远端服务器可达或分流符合预期。

v2ray test -c config.json

xray run -test -config config.json

桌面系统安装了 curl 时,可以使用下面的显式代理请求测试本例。第一条让 SOCKS 代理接收域名,适合检查 full:example.org 拒绝规则;第二条通过 HTTP 入口访问另一个文档示例域名,应走直连。两条命令均需在内核已正常启动、端口与本例一致的前提下执行。

curl --proxy socks5h://127.0.0.1:10808 --connect-timeout 10 https://example.org/

curl --proxy http://127.0.0.1:10809 --connect-timeout 10 https://example.com/

第一条预期连接失败,但单看失败提示无法区分拒绝规则、入口未启动或网络故障;第二条是否成功也取决于实际网络。应把命令结果与内核日志一起判断,不能仅凭网页未打开就认定某条规则已命中。

八、读配置时的常见疑问

导入订阅后,为什么不能把订阅内容直接存成 config.json?

订阅是客户端识别和更新节点的数据来源,不一定是内核可直接加载的完整配置。它可能包含分享链接或编码后的列表,也通常不负责定义本机监听入口。应先由客户端导入,再查看客户端生成的运行配置,不能只修改文件扩展名。

SOCKS 与 HTTP 两个端口可以交换吗?

端口数字本身没有绑定协议,配置里可以调整,但应用侧必须同步修改。把 HTTP 代理地址填成本文的 10808,不会因为端口能连接就自动变成 HTTP 入站。先核对 protocol,再核对端口与应用使用的代理类型。

为什么拒绝规则已经存在,请求却仍然直连?

按顺序检查四点:应用是否真的走代理入口;内核收到的是域名还是 IP;更早的规则是否已经匹配;当前运行的是否是刚修改的配置。还要注意,精确域名规则不会覆盖子域名。不要一开始就通过更换节点掩盖规则问题。

这份示例可以直接替换 v2rayNG 的运行配置吗?

不应直接替换。安卓客户端还需要结合 VPN 接入、DNS 和应用分流等设置生成运行内容,本文只演示本地 SOCKS、HTTP 与路由的基础结构。实际调整应使用客户端相应入口,并保留可恢复的配置;需要完整代理能力时,还必须提供有效的远端出站。

客户端下载选择系统与架构