V2Ray config.json 结构拆解:inbounds、outbounds 与 routing 逐段读懂

从入站监听、出站标签到路由匹配顺序,逐段解释 V2Ray 配置文件 JSON 各字段的含义,读完能看懂客户端生成的配置。

本文速览

适合已经导入订阅、想进一步阅读 config.json 的 Mac 用户。先用一份仅含本机入站、直连与阻断的完整配置建立结构概念,再逐项检查端口、出站标签、规则顺序与 DNS。示例端口为明确指定的教学值,不代表所有客户端的默认设置。

config.json 与 v2rayN 设置的边界

config.json 是内核读取的配置文件,v2rayN 是管理节点、订阅和运行状态的桌面客户端。两者不是同一层:客户端可以把界面设置转换成内核运行配置,也可以管理自定义配置。看到一个名为 config.json 的文件,不能仅凭文件名认定它就是当前进程正在读取的文件。

本文采用 V2Ray 传统 JSON 配置结构。Xray 与 V2Ray 有相近的字段,但协议支持和具体参数并不完全相同。阅读配置前,应从客户端运行日志确认实际启动的内核、配置路径与启动错误;不要把适用于某一内核的协议参数直接粘贴到另一内核。

应用请求 本地入站 路由匹配 目标出站 远端服务

这条链路也说明了系统代理的作用:它让遵循系统代理设置的应用把请求送到本地入站。单独启动内核,不会让所有应用自动进入这条链路;不读取系统代理的应用,需要自身支持的代理选项或其他明确的接管方式。

完整示例:两个入站、两个出站

下面是一份用于本机理解结构的配置。它提供 SOCKS 与 HTTP 两个入口,默认直连,并阻断示例域名 example.com 及其子域名。它没有配置远程代理服务器,因此不能把它当作订阅节点配置。开始测试前,先确认这两个端口没有被正在运行的客户端占用。

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "local-socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": false
      }
    },
    {
      "tag": "local-http",
      "listen": "127.0.0.1",
      "port": 10809,
      "protocol": "http",
      "settings": {}
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "blocked",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "domain": [
          "domain:example.com"
        ],
        "outboundTag": "blocked"
      },
      {
        "type": "field",
        "ip": [
          "10.0.0.0/8",
          "172.16.0.0/12",
          "192.168.0.0/16"
        ],
        "outboundTag": "direct"
      }
    ]
  }
}

从最外层看,入站、出站和规则都是数组。方括号内可以放多个对象;对象之间用逗号分隔,最后一项后面不加逗号。为兼容常规 JSON 工具,保存时使用双引号,不插入注释,也不要把端口写成带引号的字符串。

10808
示例 SOCKS 入站端口
10809
示例 HTTP 入站端口
2 条
按顺序检查的路由规则

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

listen 决定接收连接的网络接口。示例使用 127.0.0.1,只有本机能连接。把它改成 0.0.0.0 会监听所有 IPv4 接口,但不等于其他设备一定能访问,还要考虑防火墙、网络隔离和认证。仅在本机使用时,保留回环监听更容易控制访问范围。

SOCKS 入站

标签
local-socks
监听
127.0.0.1:10808
协议
socks
认证
noauth,仅本机使用
UDP
false,本例关闭

应用需选择 SOCKS 代理;不能把这个端口当成 HTTP 代理端口填写。

HTTP 入站

标签
local-http
监听
127.0.0.1:10809
协议
http
参数
settings: {}

可接收 HTTP 代理请求及 CONNECT 隧道请求;入口本身不等于 TLS 加密监听。

在 macOS 中,可进入「系统设置」→「网络」→当前网络连接→「详细信息」→「代理」核对地址与端口。采用本例 HTTP 入站时,网页代理与安全网页代理可按需要指向 127.0.0.1:10809;采用 SOCKS 入站时,对应端口是 10808。排查期间只启用需要的一种方式,避免多种设置交叉影响。

tag 是内核配置内部的标识,不是服务器地址,也不是任意应用都能识别的名称。路由中的 inboundTag 可以用它区分请求来源。例如一条规则只处理 local-http,另一条规则处理 local-socks,就能按入口分别制定策略。

outbounds:协议决定行为,tag 连接路由

示例中的 freedom 是直连出站,由当前设备直接访问目标;blackhole 用于阻断请求。真正的远程代理出站还需要协议对应的服务器地址、端口与身份参数,以及可能存在的传输层设置。仅把 tag 改成 proxy,不会把直连变成代理。

字段或协议含义检查重点
tag出站的内部名称与规则的 outboundTag 完全一致
protocol选择出站实现确认运行内核支持该协议
settings该协议的专属参数不同协议不能套用同一结构
streamSettings传输与传输安全设置与服务端要求逐项对应
freedom直接连接目标使用本机的实际网络出口
blackhole阻断匹配请求确认没有误匹配正常业务

在常规配置中,没有命中路由规则的请求会使用 outbounds 数组的第一个出站。本例将 direct 放在第一项,所以未命中请求直连。阅读客户端生成文件时,这个顺序尤其重要:文件里存在一个代理出站,并不说明未匹配流量必然使用它。

结论:先确认默认出站,再检查局部规则

排查“为什么仍然直连”时,先看 outbounds 第一项,再核对命中规则的 outboundTag。不要根据节点名称或界面颜色推断实际出口。

routing:首条匹配、条件组合与域名策略

routing.rules 按从上到下的顺序检查,命中一条就采用该条指定的出站,不再继续向下寻找更精确的规则。因此,明确的例外通常放在宽泛规则之前。数组顺序是配置行为的一部分,调整顺序可能在没有语法错误的情况下改变出口。

  1. 请求目标为 www.example.com:第一条的 domain:example.com 匹配主域名及其子域名,交给 blocked。
  2. 请求目标为 192.168.1.20:第一条不匹配,第二条匹配私网地址段,交给 direct。
  3. 目标不符合上述条件:没有规则命中,使用第一个出站 direct。

同一条规则中,不同条件字段通常需要同时满足;同一字段的多个候选值则按任一匹配处理。例如,下面这条规则只阻断从 HTTP 入站进入、目标端口为 25 的 TCP 请求。SOCKS 入站上的同端口请求不符合它的全部条件。

{
  "type": "field",
  "inboundTag": ["local-http"],
  "network": "tcp",
  "port": "25",
  "outboundTag": "blocked"
}

这段对象应放进已有的 routing.rules 数组,并按需要安排位置;它不是完整配置。注意字段类型也有差别:入站监听的 port 是数字,而路由规则的 port 使用字符串表达端口或范围,例如 "80,443"。

使用 geosite: 或 geoip: 类条件时,还要确认内核能读取相应的数据文件,且条目名称存在。本文使用明确的域名与 CIDR 网段,便于把规则行为与外部数据依赖分开观察。不要把 routing.domainStrategy 与直连出站中同名或相近的 DNS 策略选项混为一谈。

DNS 与 sniffing:规则能看到什么目标

域名规则能否命中,取决于入站收到的目标信息。HTTP 代理请求与 CONNECT 通常携带域名;SOCKS 请求既可以传域名,也可以传应用已经解析好的 IP。如果应用先在本地解析,内核收到的可能只有地址,不能假设它一定知道原来的域名。

curl --proxy socks5h://127.0.0.1:10808 --connect-timeout 5 https://example.com/
curl --proxy http://127.0.0.1:10809 --connect-timeout 5 https://example.com/

第一条命令的 socks5h 让 curl 把域名交给 SOCKS 代理处理;若改用 socks5,curl 会在本地解析目标。本例预期上述域名请求被阻断,因此命令失败是教学规则的预期结果,具体错误提示取决于连接阶段。这里的 5 是连接超时秒数,不是性能测量结果。

配置修改顺序与常见错误定位

先备份,再确定正在生效的文件,最后修改。v2rayN 可能在切换节点、更新订阅或重启内核时重新生成运行配置。临时修改生成文件适合验证问题,不适合长期保存规则;需要保留的变更应回到客户端对应设置或受管理的自定义配置中。

  1. 确认内核与路径。查看启动日志里的内核名称、加载文件和错误行。路径包含空格时,终端命令中的文件路径要使用引号。
  2. 检查 JSON 结构。在 macOS 终端执行下方命令。它只解析 JSON,不验证内核是否支持每个字段。
  3. 检查监听端口。确认 10808、10809 未被其他进程占用,并与应用设置对应。
  4. 核对标签引用。逐项检查 outboundTag 是否指向已有出站,inboundTag 是否与入站标签一致。
  5. 重载并验证。从客户端重启或重载内核,确认启动成功,再分别测试已知命中与已知不命中的目标。
plutil -lint -s -r -- config.json
lsof -nP -iTCP:10808 -sTCP:LISTEN
lsof -nP -iTCP:10809 -sTCP:LISTEN

plutil 在这里用于 JSON 语法检查;即使语法通过,协议拼写错误、缺少必要参数或内核不兼容仍会导致启动失败。若监听检查没有输出,也不能单独认定配置语法错误,还要查看进程是否启动、是否读取了另一份文件。

现象优先检查下一步动作
内核启动即退出JSON、字段兼容性、必填参数读取启动日志的第一条有效错误
提示地址已占用入站端口及重复进程定位监听者,再决定停用或改端口
规则没有命中规则顺序、目标形式、入站标签用固定代理命令复现并对照日志
改动重启后消失是否修改了生成文件将设置迁回客户端管理入口
浏览器可用,其他应用失败应用是否遵循系统代理检查应用自身代理类型与端口

结论:一次只修改一个层级

先验证入站能接收请求,再确认出站可用,最后调整路由。短时提高日志详细程度有助于排查,结束后恢复原级别;分享日志和配置前移除订阅地址、身份凭据及私人目标信息。

下载v2rayN按平台选择客户端