适合已经完成订阅导入、需要调整 v2rayN 路由分流的用户。本文按 V2Ray/Xray 的 routing 配置解释域名、IP、分类数据与规则顺序,并特别区分“关键词匹配”与并不存在的原生 keyword: 前缀。读完可编写最小规则、识别被上层规则遮蔽的例外,并用实际连接日志验证出站。
1. 路由规则处理的是连接,不是订阅节点
订阅导入解决服务器地址、认证与传输参数;路由分流决定某条连接交给哪个出站。VMess、VLESS 是代理协议,routing.rules 是流量选择规则,两者不能互相替代。订阅更新成功,也不代表自定义路由已经被当前配置启用。
路由看到的目标可能是域名,也可能只有 IP 地址。应用通过代理协议传入域名时,可以直接执行域名规则;应用自行解析后只交出 IP 时,域名规则未必有匹配依据。嗅探在支持的流量上可能恢复域名信息,但不能把任意加密流量都还原成网站名称。
type: "field":本文使用的字段匹配规则类型。domain、ip、port:描述目标连接的匹配条件。outboundTag:引用已存在的出站标签,不是直接填写服务器地址。- 规则不修改节点的 TLS、传输路径或认证参数,连接失败仍需分别检查这些配置。
2. domain 写法:精确、子域、关键词与正则
domain 是字符串数组,同一数组中的条目是“或”关系,任意一条匹配即可满足这个字段。优先选择范围明确的写法:只匹配一个主机用 full:,覆盖根域及其子域用 domain:,确实需要文本搜索时才用无前缀字符串。
| 写法 | 匹配范围 | 注意事项 |
|---|---|---|
full:example.com | 仅 example.com | 不包含 www.example.com |
domain:example.com | example.com 及其子域 | 不匹配 notexample.com |
example | 域名中的子串 example | 可能同时匹配多个不相关站点 |
regexp:^api[0-9]+\.example\.com$ | 如 api1.example.com | JSON 中反斜杠需要转义 |
geosite:cn | 数据文件中 cn 分类的域名 | 取决于实际安装的数据文件 |
keyword: 不是这里的原生前缀
有些规则编辑器把关键词选项命名为 keyword,但在 V2Ray/Xray 的原生 domain 数组中,关键词匹配使用普通字符串。不要把 keyword:example 当作通用写法复制进去;它不会自动表达预期的“域名包含 example”。界面提供的类型选择与内核 JSON 是两个层次,应检查最终生成的规则。
{
"type": "field",
"domain": [
"full:login.example.com",
"domain:docs.example.com",
"regexp:^api[0-9]+\\.example\\.com$"
],
"outboundTag": "proxy"
}
这是一条规则对象,不是完整客户端配置。三个条目分别负责登录主机、文档域及其子域、带数字编号的 API 主机。正则中的 \\. 是 JSON 字符串表示法,解码后交给正则引擎的才是 \.;若在客户端的纯文本正则输入框填写,应按该输入框格式处理,不要机械地多加一层反斜杠。
结论:例外域名优先使用 full:
需要单独调整登录或支付主机时,先写精确域名并放在广泛分类之前。不要用短关键词覆盖整个站点,以免把名称相似的无关域名一起改道。
3. ip、geoip 与 geosite:地址范围和域名集合分开写
ip 接受 IP 地址、CIDR 网段以及 geoip: 数据引用;domain 接受域名规则与 geosite: 引用。两类数据不是同一个维度:域名分类说明某个名称属于哪个集合,IP 分类说明目标地址落在哪个地址集合,不能据此保证网站内容、公司归属或物理机房位置。
域名分类规则
- 字段
domain- 引用
geosite:cn- 资源
geosite.dat- 前提
- 路由可获得目标域名
分类名称必须存在于当前数据文件,不能按名称猜测可用性。
IP 分类规则
- 字段
ip- 引用
geoip:private- 资源
geoip.dat- 前提
- 目标为 IP 或获得解析结果
分类内容随数据文件变化;指定地址测试时可先用明确的 CIDR。
下面把域名直连与私有地址直连写成两条规则,表达“命中任一集合就直连”。如果把 domain 与 ip 塞进同一条规则,含义会变成同时满足两个字段,而不是二选一。
{
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"ip": ["geoip:private", "192.0.2.10/32"],
"outboundTag": "direct"
}
]
}
}
192.0.2.10/32是文档示例中的单个 IPv4 地址,不代表真实服务;测试时替换为自己的目标。- IPv4 的
/32匹配单个地址,IPv6 单地址可用/128,网段则按实际前缀长度填写。 geoip:cn不等于geosite:cn。同一域名可能通过不同解析结果落入不同 IP 集合。- 出现分类加载错误时,先确认内核运行环境能找到相应数据文件,再核对分类名。不要只反复调整规则位置。
4. 优先级:自上而下,首次命中停止
路由不会自动把“更具体”的规则排在前面。每一轮扫描都按 rules 数组顺序进行,命中一条后就选择对应出站。精确域名即使比 geosite 分类更具体,放在已经命中的分类规则下面也不会得到补充执行。
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"domain": ["full:login.example.com"],
"outboundTag": "proxy"
},
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
以上仅展示 routing 部分,假定完整配置里已经存在标签为 proxy 与 direct 的出站。它们是示例标签,不是所有客户端固定使用的名称;尤其不能把 direct 误认为只写标签就会自动创建直连出站。
- 先放明确例外,例如单个登录主机需要代理。
- 再放基础网络规则,例如明确的局域网目标。
- 随后放范围较广的域名分类或 IP 分类。
- 最后决定兜底行为;未命中任何规则时,V2Ray/Xray 通常使用出站列表的第一个出站。
字段之间通常是“且”关系,字段数组内部通常是“或”关系。例如一条规则同时写 domain: ["domain:example.com"] 和 port: "443",只处理该域及子域的目标端口 443 连接。这里的端口是目标端口,不是客户端本地监听端口。
5. domainStrategy:是否解析,以及何时解析
routing.domainStrategy 决定路由阶段如何利用域名解析结果,不是浏览器 DNS 设置,也不是出站模块里同名选项的替代品。切换它可能改变匹配结果与解析时机,但不会改变规则数组的基本优先级。
| 取值 | 路由阶段行为 | 排查重点 |
|---|---|---|
AsIs | 不为路由匹配主动解析域名 | 原始目标为 IP 时仍可匹配 ip 规则 |
IPIfNonMatch | 域名请求在第一轮未匹配后,解析并重新尝试规则 | 先命中域名规则或兜底就不会触发这一流程 |
IPOnDemand | 域名请求扫描到需要 IP 判断的规则时,按需解析 | 靠前的 IP 规则可能先于后面的域名规则命中 |
例如把 geoip:cn 放在某条域名代理规则之前:使用 IPOnDemand 时,前面的 IP 条件可能触发解析,并在地址属于该集合时直接选择直连;使用 AsIs 时,只有域名的请求不会为了这条 IP 规则主动解析,可能继续命中后面的域名规则。两次结果不同,不代表规则顺序失效,而是可参与匹配的数据不同。
再看 IPIfNonMatch:第一轮域名规则未命中,也没有其他规则接住请求时,内核才解析并重新扫描。若目标在入站时本来就是 IP,则没有“先按域名找一轮”的必要。排查时先记录输入目标类型,避免把所有连接都当作域名请求分析。
- 只需要明确域名分流时,可先使用
AsIs建立可解释的最小配置。 - 需要“域名规则优先,未命中再按 IP 判断”时,检查
IPIfNonMatch以及是否存在提前兜底。 - 需要在扫描过程中使用解析后的 IP 时,再评估
IPOnDemand,并复查靠前的广泛 IP 分类。 - 路由阶段的解析使用内核 DNS 机制;解析结果错误、失败或与应用原先解析不一致,都可能影响分类判断。
6. v2rayN 自定义规则生效验证
图形界面里的路由方案、正在运行的内核配置与磁盘上的编辑文件可能不是同一份内容。操作前先保存当前路由方案;在 v2rayN 主窗口的路由配置入口确认当前方案,再检查生成配置中的 routing.domainStrategy 与 routing.rules。不同界面布局的菜单名称可能变化,应以字段与实际运行配置为准,不要直接编辑临时生成文件后期待长期保留。
- 建立单条测试规则。选择自己可访问的完整主机名,用
full:放在规则顶部,并指向已存在的出站标签。先不要混入正则与分类数据。 - 保存并重新应用配置。通过客户端重新加载或重启内核,使修改进入当前运行配置。确认启动输出没有未知字段、找不到出站或分类加载失败等错误。
- 建立新连接。关闭对应网页后重新访问;必要时重启测试应用。HTTP/2 长连接与连接复用可能继续使用旧出站,刷新页面不一定建立新连接。
- 查看实际日志。在启用访问日志时核对目标与出站标签;必要时短时提高日志级别。日志格式随内核不同,不应仅凭网页能否打开推断命中了哪一条规则。
- 逐项恢复复杂条件。精确域名通过后,再增加域名分类、IP 条件与 DNS 策略,每次只改一个变量,保留前后日志对照。
浏览器请求还可能涉及主站、静态资源、登录接口与媒体域名。一条网页加载成功不代表所有子连接都经过同一出站。检查日志时以具体目标主机为单位,不要把一个页面的所有请求归为同一条连接。
写了 domain:example.com,怎么另一个域名也跟着走了?
先检查实际保存的条目有没有漏掉 domain:。普通字符串 example.com 是子串匹配,范围更宽;需要只匹配一个主机时改成 full:example.com,重新应用后建立新连接。
geosite 规则保存了,为什么内核启动时报错?
从启动输出确认是否提示分类不存在或数据文件无法读取,再核对当前内核的数据资源位置和实际分类名。先换成一条 full: 规则验证基础路由,不要把资源加载失败当作节点不可用。
开了 IPIfNonMatch,IP 规则还是不执行?
检查前面是否已有域名规则命中,以及末尾是否有 network: "tcp,udp" 这样的兜底。只要第一轮已经匹配,就不会因为这一策略再解析重试;随后检查请求进入内核时究竟是域名还是 IP。
把端口 53 设为直连,就能控制全部 DNS 吗?
不能。目标端口 53 只覆盖符合条件的传统 DNS 连接,加密 DNS 可能使用其他端口,内核自身 DNS 查询也有单独的处理路径。应同时检查应用 DNS 设置和内核 DNS 配置,而不是只加一条端口规则。
结论:用目标、顺序、出站三项记录验证
每次修改记录“测试主机名、规则位置、日志中的出站标签”。精确规则验证通过后再扩展到分类集合,能把语法错误、匹配范围错误与 DNS 时机问题分开处理。