V2Ray 自定义路由规则写法:domain、ip、geosite 匹配语法与规则匹配顺序

逐条讲解 domain、full、regexp、geosite、geoip 等匹配前缀的写法,说明 routing 规则自上而下的匹配顺序和 domainStrategy 对匹配结果的影响。

本文速览

本文面向需要手动调整分流的用户。先区分域名、IP 与数据集的匹配语法,再通过一组 routing 示例理解规则顺序、字段组合与 DNS 解析时机。排查时以客户端实际生成的内核配置为准,不把界面里的“直连”“代理”名称直接当成 outboundTag。

1. 路由匹配的对象与执行顺序

V2Ray 的 routing 根据连接的目标地址、目标端口、网络类型和入站标签等信息选择出站。它不是网页内容过滤器,也不按 URL 路径分流。访问同一域名下的两个不同页面,通常不会因此命中不同的域名规则。

本地入站 目标识别 规则匹配 出站选择 建立连接

routing.rules 按数组顺序从上到下判断。某条规则满足条件后,便按它指定的出站处理;后面的规则不会再与它比较“谁更精确”。因此,精确域名例外通常放在域名后缀规则前,具体规则放在兜底规则前。

2. domain、full、regexp 与 geosite 的写法

domain 是规则字段名,domain: 则是该字段内的一种匹配前缀。两者不要混淆。下面全部使用保留示例域名 example.com,用于说明语法,不代表真实服务的分流建议。

条目写法匹配含义注意事项
full:example.com只匹配这个完整域名不会同时覆盖 www.example.com
domain:example.com匹配自身及其子域名覆盖 api.example.com,不覆盖 notexample.com
example.com未加前缀时按子串匹配可能匹配域名中包含此字符串的其他名称
regexp:^api[0-9]+\.example\.com$按正则表达式匹配写入 JSON 时,反斜杠需要再次转义
geosite:cn引用域名数据文件中的 cn 分类分类内容取决于实际加载的数据文件

希望覆盖某个站点及其子域名时,优先写 domain:example.com,而不是依赖裸字符串。需要只处理一个主机名时,再用 full:。域名条目不应包含 https://、端口、路径或查询参数;这些不是该字段的输入格式。

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

这段 JSON 中,三个域名条目是“或”的关系。正则中的 \\. 经过 JSON 解码后才成为正则所需的 \.;如果只写一条反斜杠,配置可能在 JSON 解析阶段就失败。正则应遵循内核所用的 Go 正则语法,不使用回溯引用或前后向断言。

判断:单站点规则先用 full 或 domain

只有域名结构确实需要模式表达时再用 regexp。修改后分别测试主域名、一个子域名和一个相似但无关的域名,避免把子串误匹配当成规则生效。

3. ip、geoip 与目标端口如何组合

ip 字段接受单个 IP、CIDR 网段以及 geoip: 数据集引用。IPv4 的 /32 表示一个地址;192.0.2.0/24 表示一个文档示例网段。不要把 CIDR 写入 domain 数组,也不要在 ip 数组里添加 https://。

/32
IPv4 单地址前缀长度
/128
IPv6 单地址前缀长度
443
下例匹配的目标端口
{
  "type": "field",
  "ip": [
    "192.0.2.0/24",
    "2001:db8::/32"
  ],
  "port": "443",
  "network": "tcp",
  "outboundTag": "direct"
}

这条规则只匹配目标地址位于两个示例网段之一、目标端口为 443、网络为 TCP 的连接。它不会匹配同地址的 TCP 80,也不会匹配 UDP 443。这里的 port 是目标端口,不是客户端的本地监听端口;本地入站需要按标签区分时,应使用 inboundTag。

4. domainStrategy 决定何时为路由解析域名

routing.domainStrategy 控制路由阶段如何处理域名目标。它不等于系统 DNS 设置,也不等于出站自身的同名或近似设置。即使路由阶段不解析,后续出站为了连接服务器仍可能需要解析地址。

取值路由阶段行为常见影响
AsIs按原始目标进行匹配,不为路由额外解析域名只有域名的目标不会因此获得可供 IP 规则匹配的解析结果
IPIfNonMatch域名阶段没有规则命中时,再尝试解析并重新匹配前面的域名命中或通用兜底可能使解析阶段不再触发
IPOnDemand匹配过程中遇到需要 IP 判断的规则时,尝试解析域名较早的 IP 规则可能先于后面的域名规则决定结果

一个容易忽略的情况是:使用 IPIfNonMatch,却在末尾添加匹配所有 TCP、UDP 的规则。域名请求会直接命中这个兜底,因此不能再指望“前面的 geoip 没命中,随后自动解析再试”。如果需要域名规则优先、随后按解析 IP 分流,应一起审查解析策略和兜底方式。

域名优先,未命中再解析

domainStrategy
IPIfNonMatch
规则起始
明确的域名例外
后续条件
需要补充判断的 IP 规则
兜底检查
避免通用规则提前命中

确认全部规则未命中时,第一个出站正是预期的默认出站。

按规则顺序按需解析

domainStrategy
IPOnDemand
规则起始
需要优先保留的域名例外
后续条件
IP 或 geoip 条件
兜底检查
可在 IP 判断后显式兜底

先测试 DNS 能否返回结果,再检查 IP 分类是否符合预期。

如果应用交给内核的目标本来就是 IP,路由不需要靠这项设置获得该地址。但此时域名信息可能不存在。入站的嗅探功能可以在部分协议中补充域名线索,其效果受流量类型和具体配置限制,不能认为开启后所有连接都会恢复域名。

5. 一组可核对顺序的 routing 示例

下面是配置片段,不是可单独运行的完整配置。前提是已有出站标签 proxy、direct 和 block,分别指向代理、直连和阻断出站。标签由配置作者定义,不是内核自动创建的保留名称。此例还要求可用的数据文件包含 private 分类。

{
  "routing": {
    "domainStrategy": "IPOnDemand",
    "rules": [
      {
        "type": "field",
        "domain": ["full:status.example.com"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["domain:example.com"],
        "outboundTag": "proxy"
      },
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "port": "6881-6889",
        "outboundTag": "block"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}
  1. 精确例外在前:status.example.com 命中第 1 条后直连,不会继续落入第 2 条的整个域名后缀规则。
  2. 站点规则在后:www.example.com 命中第 2 条,选择 proxy。
  3. 内网地址优先:前两条均未命中时,私有地址可命中第 3 条。域名目标在这里可能触发按需解析。
  4. 端口规则不是全局禁令:第 4 条只处理此前未命中的连接。前面已命中的站点或私有地址不会再进入它。
  5. 显式兜底在末尾:剩余 TCP、UDP 连接命中第 5 条。这里使用 IPOnDemand,使前面的 IP 判断有机会先执行。

操作:把必须全局执行的限制移到前面

如果端口 6881–6889 必须优先阻断,应把端口规则移到域名和私有地址规则之前。调整的是数组位置,不是给规则添加一个不存在的“优先级数字”。

使用 geosite:cn 时,可按实际需求增加一条域名规则,但不要因为分类名称熟悉就直接覆盖所有例外。先确认数据文件可加载,再判断这条规则应位于精确域名例外之后、IP 规则之前还是其他位置。数据更新也可能改变分类命中结果。

6. 从客户端界面到实际生效配置的检查方法

v2rayN、v2rayNG、v2flyNG 的界面名称和路由导入方式随版本变化。排查时先记录客户端版本、内核名称和所用路由模式,再定位当前启用的规则集。不要把订阅节点中的 VMess、VLESS 连接参数当成路由规则;节点负责定义出站连接,路由负责选择它。

  1. 备份当前规则:保存修改前的路由内容。订阅更新通常用于更新节点,不应被当成分发本地自定义路由的通用机制。
  2. 确认请求进入内核:检查应用是否使用系统代理或指定的本地代理入口。未进入入站的请求,不会受这些规则控制。
  3. 核对生成配置:查看当前 routing.rules 的实际顺序,以及 outboundTag 是否与 outbounds[].tag 完全一致。
  4. 应用并重新测试:保存规则后按客户端要求重新加载或重启内核,再新建连接。浏览器连接复用可能让旧连接继续沿用原来的出站。
  5. 检查日志:需要时临时使用 info 级别,观察入站、目标和所选出站;不同内核的日志格式不同,不一定显示具体规则序号。

建议一次只改一个条件。先用 full: 验证一个明确域名能否分流,再换成 domain: 扩大范围,最后添加 geosite 或 geoip 分类。需要查找配置对象的上下文时,可配合配置参考核对入站、出站与 DNS 字段。

7. 路由规则常见问题

加了域名规则,为什么还是走原来的节点?

先查看它之前是否已有更宽泛的规则命中。临时把测试用的 full: 规则放到首位,重新加载内核并新建连接。若仍不生效,再确认请求目标是域名还是 IP,以及流量是否真正进入当前客户端。

geosite 或 geoip 提示分类找不到,应该改哪里?

检查当前内核实际使用的数据文件及其中的分类名称。geosite: 应放在 domain,geoip: 应放在 ip。先用单个域名或明确网段验证规则结构,再处理数据资源缺失,不要反复修改节点密码。

同一条规则同时填了 domain 和 ip,为什么更难命中?

两个字段需要同时满足,不是二选一。若目的只是“这个域名或这个网段都直连”,应拆成两条规则,并分别指定同一个 outboundTag。拆分后再检查域名解析策略是否允许 IP 条件参与判断。

改成 IPIfNonMatch,geoip 仍然没效果?

检查末尾是否存在 network: "tcp,udp" 这类通用匹配。它可能在域名阶段已经命中,阻止“未命中后解析”的流程。可根据需求调整为按需解析,或移除提前兜底并核对默认出站;两种改法都应重新测试域名和 IP 目标。

下载客户端