V2Ray 自定义路由规则写法:domain、ip、geosite 匹配语法与生效优先级

讲清 domain 的 full/domain/keyword/regexp 前缀、ip 与 geoip 写法、geosite 分类引用,以及规则自上而下匹配的顺序和 domainStrategy 的影响。

本文速览

适合已经完成订阅导入、需要调整 v2rayN 路由分流的用户。本文按 V2Ray/Xray 的 routing 配置解释域名、IP、分类数据与规则顺序,并特别区分“关键词匹配”与并不存在的原生 keyword: 前缀。读完可编写最小规则、识别被上层规则遮蔽的例外,并用实际连接日志验证出站。

1. 路由规则处理的是连接,不是订阅节点

订阅导入解决服务器地址、认证与传输参数;路由分流决定某条连接交给哪个出站。VMess、VLESS 是代理协议,routing.rules 是流量选择规则,两者不能互相替代。订阅更新成功,也不代表自定义路由已经被当前配置启用。

连接进入内核 读取目标信息 规则顺序匹配 选择出站标签 建立目标连接

路由看到的目标可能是域名,也可能只有 IP 地址。应用通过代理协议传入域名时,可以直接执行域名规则;应用自行解析后只交出 IP 时,域名规则未必有匹配依据。嗅探在支持的流量上可能恢复域名信息,但不能把任意加密流量都还原成网站名称。

2. domain 写法:精确、子域、关键词与正则

domain 是字符串数组,同一数组中的条目是“或”关系,任意一条匹配即可满足这个字段。优先选择范围明确的写法:只匹配一个主机用 full:,覆盖根域及其子域用 domain:,确实需要文本搜索时才用无前缀字符串。

写法匹配范围注意事项
full:example.com仅 example.com不包含 www.example.com
domain:example.comexample.com 及其子域不匹配 notexample.com
example域名中的子串 example可能同时匹配多个不相关站点
regexp:^api[0-9]+\.example\.com$如 api1.example.comJSON 中反斜杠需要转义
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"
      }
    ]
  }
}

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 误认为只写标签就会自动创建直连出站。

  1. 先放明确例外,例如单个登录主机需要代理。
  2. 再放基础网络规则,例如明确的局域网目标。
  3. 随后放范围较广的域名分类或 IP 分类。
  4. 最后决定兜底行为;未命中任何规则时,V2Ray/Xray 通常使用出站列表的第一个出站。

字段之间通常是“且”关系,字段数组内部通常是“或”关系。例如一条规则同时写 domain: ["domain:example.com"] 和 port: "443",只处理该域及子域的目标端口 443 连接。这里的端口是目标端口,不是客户端本地监听端口。

443
示例 HTTPS 目标端口
53
传统 DNS 常用目标端口
/32
IPv4 单地址前缀长度

5. domainStrategy:是否解析,以及何时解析

routing.domainStrategy 决定路由阶段如何利用域名解析结果,不是浏览器 DNS 设置,也不是出站模块里同名选项的替代品。切换它可能改变匹配结果与解析时机,但不会改变规则数组的基本优先级。

取值路由阶段行为排查重点
AsIs不为路由匹配主动解析域名原始目标为 IP 时仍可匹配 ip 规则
IPIfNonMatch域名请求在第一轮未匹配后,解析并重新尝试规则先命中域名规则或兜底就不会触发这一流程
IPOnDemand域名请求扫描到需要 IP 判断的规则时,按需解析靠前的 IP 规则可能先于后面的域名规则命中

例如把 geoip:cn 放在某条域名代理规则之前:使用 IPOnDemand 时,前面的 IP 条件可能触发解析,并在地址属于该集合时直接选择直连;使用 AsIs 时,只有域名的请求不会为了这条 IP 规则主动解析,可能继续命中后面的域名规则。两次结果不同,不代表规则顺序失效,而是可参与匹配的数据不同。

再看 IPIfNonMatch:第一轮域名规则未命中,也没有其他规则接住请求时,内核才解析并重新扫描。若目标在入站时本来就是 IP,则没有“先按域名找一轮”的必要。排查时先记录输入目标类型,避免把所有连接都当作域名请求分析。

6. v2rayN 自定义规则生效验证

图形界面里的路由方案、正在运行的内核配置与磁盘上的编辑文件可能不是同一份内容。操作前先保存当前路由方案;在 v2rayN 主窗口的路由配置入口确认当前方案,再检查生成配置中的 routing.domainStrategy 与 routing.rules。不同界面布局的菜单名称可能变化,应以字段与实际运行配置为准,不要直接编辑临时生成文件后期待长期保留。

  1. 建立单条测试规则。选择自己可访问的完整主机名,用 full: 放在规则顶部,并指向已存在的出站标签。先不要混入正则与分类数据。
  2. 保存并重新应用配置。通过客户端重新加载或重启内核,使修改进入当前运行配置。确认启动输出没有未知字段、找不到出站或分类加载失败等错误。
  3. 建立新连接。关闭对应网页后重新访问;必要时重启测试应用。HTTP/2 长连接与连接复用可能继续使用旧出站,刷新页面不一定建立新连接。
  4. 查看实际日志。在启用访问日志时核对目标与出站标签;必要时短时提高日志级别。日志格式随内核不同,不应仅凭网页能否打开推断命中了哪一条规则。
  5. 逐项恢复复杂条件。精确域名通过后,再增加域名分类、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 时机问题分开处理。

v2rayN 下载平台与安装包选择