本文速览

适合已经能够连接节点、准备修改分流规则的用户。本文从域名与 IP 字段的区别开始,用一组五条规则解释匹配顺序、出站标签与 DNS 解析策略,再给出 v2rayN 中的调整和验证步骤。重点是判断连接为什么命中某条规则,而不是仅凭网页能否打开猜测配置是否生效。

连接进入内核后,路由规则才开始工作

系统代理、TUN 接管与 Xray 路由属于不同阶段。系统代理决定支持该设置的应用是否把连接送到本地入口;路由规则处理已经进入内核的连接,再选择直连、代理或阻断。浏览器绕过了系统代理时,即使路由规则写得完全正确,也不会产生对应的路由结果。

应用连接 入站接收 目标识别 规则匹配 出站转发

本文使用 Xray 的原生 JSON 字段。v2rayN 和 v2rayNG 的界面会将设置转换为内核配置;v2flyNG 使用 V2Fly 内核,不能据此假定所有 Xray 字段均可直接照搬。编辑前先确认当前使用的内核,并备份正在使用的路由方案。

domain、full 与 regexp:先限定匹配范围

最常见的误分流来自匹配范围过宽。只写一个裸字符串时,Xray 按子串匹配处理;需要“这个域名及其子域名”时,应明确使用 domain:。规则中填写主机名,不要粘贴带协议、路径或查询参数的完整网址。

写法匹配含义边界示例
example.com域名中的子串匹配可能同时命中 notexample.com
domain:example.com该域名与各级子域名命中 example.com、img.example.com,不命中 notexample.com
full:example.com完整域名精确匹配不包含 www.example.com
regexp:^img[0-9]+\.example\.com$正则表达式匹配命中 img12.example.com,不命中 img.example.com
geosite:cn引用域名数据文件中的 cn 集合结果取决于当前数据文件,不能等同于所有境内服务器

正则还有一层 JSON 转义。路由编辑框中的原始表达式可以包含 \.;写入 JSON 字符串时,反斜杠必须写成 \\。下面展示的是可解析的 JSON 规则对象,不是可以单独启动内核的完整配置。

{
  "type": "field",
  "domain": [
    "full:example.com",
    "domain:example.net",
    "regexp:^img[0-9]+\\.example\\.com$"
  ],
  "outboundTag": "direct"
}

ip、geoip 与 geosite:数据来源不同

geosite 匹配域名集合,geoip 匹配 IP 地址集合。网站使用多个内容分发地址时,域名分类与实际连接地址的地域分类可能不同。想让某个具体站点固定走代理,应优先写域名例外,不要只依赖 IP 所属地区。

{
  "type": "field",
  "ip": [
    "192.168.0.0/16",
    "10.0.0.0/8",
    "geoip:private",
    "geoip:cn"
  ],
  "outboundTag": "direct"
}

上例前两项演示 CIDR 写法,与 geoip:private 的部分覆盖范围重叠;实际使用可以删去重复项。只写 IPv4 私网段不会自动涵盖 IPv6 本地地址,双栈网络应检查所用私有地址集合的覆盖范围。

规则优先级:从上到下,命中即停止

在一次路由匹配过程中,Xray 按 rules 数组顺序检查,使用第一个满足全部条件的规则。不会因为后面的域名更具体,就自动选择后面的规则。精确例外应放在宽泛分类之前,兜底规则放在末尾。

同一字段的数组通常是“或”:域名数组中任一表达式命中即可。同一条规则中的不同筛选字段通常是“且”:同时设置 domain、port 和 network,就要求这些条件一起满足。把域名条件和 IP 条件写在同一条规则里,不等于“命中任意一个就直连”。

{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "domain": ["full:example.com"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["geosite:cn"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "ip": ["geoip:private", "geoip:cn"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

这是便于讲解顺序的 routing 配置片段,假定已有 direct、proxy、block 三个出站,且数据文件提供示例集合。标签名称不是内核保留的动作名称:常见直连出站采用 freedom,阻断出站采用 blackhole,代理出站则引用实际节点协议。

5 条
示例规则按数组顺序执行
3 个
需要已存在的出站标签
443
可用于 HTTPS 端口条件测试

先确定例外是否应覆盖拦截

如果一个域名必须直连,即使它也被广告集合收录,就把它的精确直连规则移到广告规则之前;只放在 cn 集合之前还不够。

domainStrategy:域名何时参与 IP 匹配

IP 规则能否用于一个域名目标,取决于路由的 domainStrategy。该字段控制路由阶段是否为匹配而解析域名,不等于整个客户端的 DNS 设置,也不等于直连出站最终如何解析目标地址。

AsIs:保留目标类型

字段
routing.domainStrategy
取值
AsIs
域名目标
不为路由匹配主动解析为 IP
IP 目标
可以直接参与 IP 规则匹配

适合先验证域名规则,避免将 DNS 解析结果混入第一次排查。

IPIfNonMatch:无匹配再解析

字段
routing.domainStrategy
取值
IPIfNonMatch
触发条件
域名目标在第一轮没有规则匹配
后续操作
解析后再次尝试匹配,纳入 IP 规则

末尾的全匹配兜底会使第一轮已有结果,不能期待它之后再触发 IP 补判。

因此,上一节的五条规则虽然写了 IPIfNonMatch,但域名目标如果走到最后一条 TCP/UDP 兜底,第一轮已经匹配成功,不会再为前面的 geoip:cn 触发解析。原本就是 IP 的目标仍可命中第四条。这是“已写 geoip,域名却仍走代理”的常见原因。

另外,若入口收到的只有目标 IP,域名规则并不会凭空知道原始主机名。嗅探在可识别流量中可能补充域名信息,但它不是任意协议都有效的域名恢复机制。排查时要同时看入口目标、嗅探设置与最终出站。

v2rayN 中调整规则顺序与验证结果

在 v2rayN 中打开「设置」→「路由设置」,选择准备使用的路由方案后进入规则编辑。不同界面版本的编辑、上移和下移按钮位置可能不同,判断依据是最终生成的 routing.rules 顺序,而不是方案名称或规则备注。

  1. 复制方案:保留原方案作为回退,只在副本中添加一条针对测试域名的 full: 规则。先不同时调整 DNS、嗅探和内核。
  2. 分开填写:将 full:example.com 放到域名条件,将 geoip:cn 放到 IP 条件;界面以逐行输入时,每行填写一个匹配项,不要直接粘贴整个 JSON 对象。
  3. 核对出站:在出站选项中选择所需出口。若使用原生配置,检查 outboundTag 与出站 tag 完全一致,包括大小写。
  4. 调整先后:将测试规则移到可能提前命中的规则上方,保存后确认选中了这个路由方案,并按界面提示重新加载或重启内核。
  5. 建立新连接:关闭旧网页连接后重新访问。已有长连接通常不会因规则修改而自动切换出口;必要时关闭浏览器再测试。
  6. 记录日志:确认核心启动成功,再观察测试目标的访问记录与出站标签。必要时在日志设置中临时提高详细程度,测试完恢复,避免持续记录大量连接信息。

如果要验证“仅 HTTPS 直连”,可在测试规则中同时设置域名条件、port: "443" 与 network: "tcp"。这时使用 UDP 的 HTTP/3 不满足 TCP 条件,不能仅凭浏览器打开了同一个站点就认定规则失效。协议和端口必须与测试条件一致。

写了 full,为什么 www 域名还是走代理?

full:example.com 不包含 www.example.com。先在日志中确认实际目标;需要涵盖子域名时改用 domain:example.com,只需要这两个主机时则添加两项 full 匹配。

规则放在最后,为什么一直没效果?

检查它上方有没有 network: "tcp,udp"、覆盖该域名的集合或更宽的域名匹配。把测试规则移到这些规则之前,重新加载配置并建立新连接。

更新 geo 数据后,内核反而启动失败?

查看启动日志中引用的文件与集合名称。确认数据文件路径可读、集合确实存在;先移除报错的集合引用验证启动,再修复数据来源,不要反复更换节点掩盖配置错误。

网页能打开,能证明 direct 命中了吗?

不能。直连和代理都可能成功打开页面。应核对核心访问日志中的目标与出站标签;若当前日志没有显示规则编号,就对比修改前后的出站,并一次只改变一条规则。