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

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

本文速览

本文面向准备编辑 Xray 路由规则的用户。从域名与 IP 的输入差异开始,解释规则语法、字段组合和解析策略,再给出可核对的 routing 片段。读完可以判断一条连接为什么走直连、代理或阻断,以及修改规则后应该查看哪些日志。示例使用保留测试域名,不涉及真实服务凭据。

路由输入与出站标签

Xray 的路由负责选择出站,不负责替应用创建请求。应用连接先进入 inbounds,路由读取目标地址、端口和网络类型等信息,再把连接交给对应的 outbounds。v2rayN 是客户端界面;本文代码描述的是 Xray 内核的 JSON 配置,不能直接当作订阅链接导入。

入站连接 目标信息 规则匹配 出站选择 远端连接

每条规则的 outboundTag 必须对应现有出站的 tag。下面使用的 direct、proxy、block 都是人为约定的标签,不是写上名称就会自动出现的功能。通常分别绑定直连出站、已配置的代理出站和阻断出站。

domain、full 与 regexp 的区别

域名匹配内容放在规则的 domain 数组中。这里的 domain 既是字段名,也可以是数组元素中的匹配前缀。字段名说明“检查域名”,前缀说明“怎样比较”。下面的 example.test 是保留测试域名,适合说明语法,不代表可直接访问的服务。

数组元素匹配方式边界说明
full:api.example.test完整域名匹配匹配该主机,不包含其子域名。
domain:example.test域名及子域名匹配包含根域名和 api.example.test,不包含 notexample.test。
example普通字符串匹配按子串匹配,范围通常比域名后缀匹配更宽。
regexp:^api[0-9]+\\.example\\.test$正则表达式匹配这是 JSON 字符串写法,反斜杠需要转义。
geosite:cn域名集合匹配读取本地域名数据文件中的指定集合。

只想让一个接口走代理,优先使用 full:。需要覆盖整个域名树,使用 domain:。不要把 *.example.test 当成通用通配符写入普通域名条目;需要通配效果时,应选择域名后缀匹配或明确的正则表达式。

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

上例两个数组元素是“或”的关系,任意一个命中即可。正则表达式中的点必须表达为字面点;在 JSON 中因此写成 \\.。首尾的 ^ 和 $ 限制匹配范围,避免意外匹配带有额外前缀或后缀的主机名。

ip、geoip 与 geosite 数据集

ip 字段用于检查目标 IP,可以填写单个地址、CIDR 网段或 geoip: 集合。域名集合放在 domain 中,IP 集合放在 ip 中。geosite 与 geoip 不是在线查询服务,也不能互换字段。

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

这段示例显式列出几个私有网段,并使用 geoip:private 集合补充覆盖。部分范围会重叠,这是为了对照语法;实际配置可以按所用数据文件的内容精简。192.168.0.0/16 表示一个网段,不是只匹配 192.168.0.0 这一个地址。

匹配优先级与字段组合

routing.rules 按数组顺序检查,第一条满足条件的规则决定出站。规则越具体,不代表内核会自动把它排得越靠前。位于前面的宽泛规则可能提前结束匹配,使后面的例外规则没有机会生效。

{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "domain": ["full:api.example.test"],
        "outboundTag": "proxy"
      },
      {
        "type": "field",
        "domain": ["domain:example.test"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

在这个片段中,api.example.test 走代理,www.example.test 走直连,其余 TCP、UDP 连接由最后一条规则交给代理。若把第二条移到第一条之前,接口域名会先命中域名树规则,改走直连。

组合位置逻辑关系实际含义
同一 domain 数组的多个条目或命中其中任意一项即可满足该字段。
同一 ip 数组的多个网段或地址落入任意一个网段即可满足该字段。
同一规则的 domain 与 port且域名和端口必须同时满足。
同一规则的 domain 与 ip且不是域名或 IP 二选一;IP 条件还依赖目标地址信息与解析策略。

结论:把例外放在集合规则之前

先写单个主机的特殊处理,再写域名树和分类集合,最后写兜底规则。若目标是“域名命中或 IP 命中就直连”,应拆成两条规则,而不是把两种字段塞进同一条。

domainStrategy 如何影响 IP 匹配

routing.domainStrategy 决定路由阶段是否为域名补充 IP 信息。它不是修改规则顺序的开关,也不是指定 DNS 服务器地址的字段。即使配置中存在 dns 段,路由也不会因此自动对每个域名执行 IP 匹配。

策略路由阶段的行为需要注意
AsIs不为了路由匹配主动解析域名。目标本来就是 IP 时,仍可匹配 IP 规则。
IPIfNonMatch域名未匹配到规则时,再尝试解析并进行包含 IP 条件的匹配。第一轮若已命中宽泛兜底,就不会因未命中而触发解析。
IPOnDemand遇到需要 IP 信息的规则时,尝试解析域名后继续判断。靠前的 IP 规则可能增加解析依赖,并影响后续域名规则能否被执行。

一个常见误区是:选择 IPIfNonMatch,在前面写 geoip:cn,最后再写覆盖全部 TCP、UDP 的代理规则,就期待所有域名先按地区 IP 分流。对于域名目标,第一轮可能直接命中最后的代理规则;既然已经匹配,就没有“未匹配后解析”的机会。

  1. 希望主要按域名集合分流:先配置清晰的域名规则,并确认仅有 IP 的请求如何处理。
  2. 希望域名无规则可匹配时才按 IP 判断:使用 IPIfNonMatch,同时检查是否有提前命中的通用规则。
  3. 希望靠前的 IP 条件也参与域名请求的决策:评估 IPOnDemand,并确认 DNS 返回结果与规则顺序符合预期。

路由解析得到的 IP 不应简单等同于最终建立连接时使用的地址。出站是否继续使用域名、在哪里解析,还受所用协议及出站设置影响。排查时应把“路由为什么选中这个标签”与“连接最终去了哪里”分开验证。

按端口建立最小验证用例

不要一次导入几十条分类规则再观察网页是否打开。先使用一个明确的入站端口、一条带端口限制的域名规则和可识别的出站标签,验证匹配链路。下面三个数字是测试配置中的明确参数,不是所有客户端版本的默认值。

10808
本例本地 SOCKS 入站端口
443
本例域名规则的目标端口
53
传统 DNS 常用目标端口
{
  "type": "field",
  "domain": ["full:api.example.test"],
  "port": "443",
  "network": "tcp",
  "outboundTag": "proxy"
}

该规则只匹配目标域名、目标端口 443 和 TCP 同时成立的连接。它不匹配同一主机的 80 端口,也不匹配 UDP 443。port 检查的是目标端口,不是本地 SOCKS 监听的 10808。浏览器若使用基于 UDP 的传输,不能用这条 TCP 规则推断其结果。

  1. 备份当前可用配置,确认核心已读取新的路由配置,而不是仍运行旧进程。
  2. 核对本地入站确实监听 127.0.0.1:10808;若使用其他端口,以实际配置为准。
  3. 在测试环境为保留域名准备解析或嗅探输入。未准备测试服务时,只检查核心能否加载规则,不把访问失败当成路由失败。
  4. 需要比较 DNS 行为时临时提高日志级别,重复一次请求,核对接收目标、DNS 查询与出站标签;测试完成后恢复原日志级别。
  5. 每次只调整一项:先改规则顺序,再改 domainStrategy,最后引入数据集。保留每一步的结果,避免多项变化互相掩盖。

v2rayN 路由排错问答

在 v2rayN 中调整路由后,还要核对当前选用的路由配置与内核类型。本文使用 Xray 的字段语义;界面中的路由编辑项可能由客户端转换后写入生成配置,不应把整段 JSON 随意粘贴到只接受域名条目的输入框。

加了 full 规则,为什么还是走原来的出站?

先把该规则移到宽泛域名集合和兜底规则之前,再重新加载配置。查看日志中的目标是否仍是域名;若目标只有 IP,就检查应用是否自行解析,以及入站嗅探是否确实提供了可用域名。

geosite 规则保存后,核心为什么启动失败?

查看核心启动日志,分别确认数据文件能否读取、集合名称是否存在,以及规则是否放进了正确字段。先用一条 full: 规则验证路由,再恢复集合规则,可以区分语法问题与数据文件问题。

配置了 IPIfNonMatch,为什么没有看到预期的 DNS 查询?

检查第一轮是否已命中域名规则或 network: tcp,udp 兜底。已有匹配就不满足“未匹配”的触发条件。需要按 IP 条件提前判断时,再评估 IPOnDemand,不要仅靠移动 IP 规则猜测结果。

同一规则写 domain 和 ip,为什么命中变少了?

这两个字段需要同时满足。若原意是域名集合或地址集合任意命中即可直连,把它们拆成两条规则,并保持相同的 outboundTag。随后分别用域名目标与 IP 目标测试。

修改生成配置后,重启客户端又恢复了,怎么办?

自动生成的配置可能在切换节点或重启核心时重建。应在客户端提供的路由配置入口保存规则,或使用其支持的自定义配置方式。重启后再次核对核心实际加载的内容,不以编辑器中曾经保存成功作为生效依据。

最终核对顺序应固定为:输入目标是什么、规则是否满足、哪条规则最先命中、标签是否存在、出站能否连接。网页打开只能证明一次访问完成,不能单独证明分流正确。保留最小测试规则与对应日志,比同时切换多个路由模式更容易定位问题。

下载v2rayN