适合已经能够连接节点、准备修改分流规则的用户。本文从域名与 IP 字段的区别开始,用一组五条规则解释匹配顺序、出站标签与 DNS 解析策略,再给出 v2rayN 中的调整和验证步骤。重点是判断连接为什么命中某条规则,而不是仅凭网页能否打开猜测配置是否生效。
连接进入内核后,路由规则才开始工作
系统代理、TUN 接管与 Xray 路由属于不同阶段。系统代理决定支持该设置的应用是否把连接送到本地入口;路由规则处理已经进入内核的连接,再选择直连、代理或阻断。浏览器绕过了系统代理时,即使路由规则写得完全正确,也不会产生对应的路由结果。
本文使用 Xray 的原生 JSON 字段。v2rayN 和 v2rayNG 的界面会将设置转换为内核配置;v2flyNG 使用 V2Fly 内核,不能据此假定所有 Xray 字段均可直接照搬。编辑前先确认当前使用的内核,并备份正在使用的路由方案。
domain是规则字段,数组中可以写domain:、full:、regexp:、geosite:等匹配表达式。ip是另一种规则字段,接受 IP、CIDR 网段及geoip:集合;不要把它误写成域名数组中的ip:前缀。outboundTag是出站引用,不是任意备注。它必须对应最终配置里已有的出站tag。
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 本地地址,双栈网络应检查所用私有地址集合的覆盖范围。
- 集合存在性:引用
geosite:cn或geoip:cn前,确认内核能读取对应的数据文件,且文件包含该集合。 - 分类更新时间:客户端配置更新和 geo 数据更新不是同一件事。订阅更新成功不代表域名分类已经更新。
- 扩展集合语法:
geosite:某集合@某属性只有在数据文件提供该属性时才有意义,不是给任意集合加后缀都能使用。 - 故障表现:数据文件缺失或集合不存在可能让配置加载失败。应先看启动日志,而不是把异常一概当成节点无法连接。
规则优先级:从上到下,命中即停止
在一次路由匹配过程中,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,代理出站则引用实际节点协议。
先确定例外是否应覆盖拦截
如果一个域名必须直连,即使它也被广告集合收录,就把它的精确直连规则移到广告规则之前;只放在 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 补判:可以取消显式全匹配兜底,并确认无规则命中时使用的第一个出站正是期望的默认出站。
- 需要遇到 IP 规则就解析:可评估
IPOnDemand。它在匹配需要 IP 信息的规则时尝试解析,但会增加 DNS 参与路由的机会。 - 只需一个站点固定出口:直接增加靠前的
full:或domain:规则,通常比调整整套解析策略更容易验证。
另外,若入口收到的只有目标 IP,域名规则并不会凭空知道原始主机名。嗅探在可识别流量中可能补充域名信息,但它不是任意协议都有效的域名恢复机制。排查时要同时看入口目标、嗅探设置与最终出站。
v2rayN 中调整规则顺序与验证结果
在 v2rayN 中打开「设置」→「路由设置」,选择准备使用的路由方案后进入规则编辑。不同界面版本的编辑、上移和下移按钮位置可能不同,判断依据是最终生成的 routing.rules 顺序,而不是方案名称或规则备注。
- 复制方案:保留原方案作为回退,只在副本中添加一条针对测试域名的
full:规则。先不同时调整 DNS、嗅探和内核。 - 分开填写:将
full:example.com放到域名条件,将geoip:cn放到 IP 条件;界面以逐行输入时,每行填写一个匹配项,不要直接粘贴整个 JSON 对象。 - 核对出站:在出站选项中选择所需出口。若使用原生配置,检查
outboundTag与出站tag完全一致,包括大小写。 - 调整先后:将测试规则移到可能提前命中的规则上方,保存后确认选中了这个路由方案,并按界面提示重新加载或重启内核。
- 建立新连接:关闭旧网页连接后重新访问。已有长连接通常不会因规则修改而自动切换出口;必要时关闭浏览器再测试。
- 记录日志:确认核心启动成功,再观察测试目标的访问记录与出站标签。必要时在日志设置中临时提高详细程度,测试完恢复,避免持续记录大量连接信息。
如果要验证“仅 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 命中了吗?
不能。直连和代理都可能成功打开页面。应核对核心访问日志中的目标与出站标签;若当前日志没有显示规则编号,就对比修改前后的出站,并一次只改变一条规则。