自定义分流先看规则顺序,再看匹配条件,最后检查 DNS 策略。本文面向已能正常连接节点、需要补充站点例外规则的用户,说明 V2Ray 与 Xray 常见 field 路由写法,以及如何在 v2rayN 中落地和验证。示例使用保留的演示域名与文档地址,不代表实际网站分类。
一、路由规则先判断条件,再选择出站
路由不是节点连接参数。VMess、VLESS 等协议负责连接服务端;路由负责决定一条已进入内核的请求交给哪个出站。v2rayN 是配置界面,实际执行规则的是当前运行的内核。下文使用 V2Ray/Xray 的 JSON 语法,不能原样粘贴到其他配置格式的编辑器。
一条常见规则包含 type: "field"、匹配字段和 outboundTag。标签必须对应现有出站的 tag。把标签写成 proxy 并不会自动创建代理节点,写成 direct 也不会自动创建直连出站。客户端生成的标签可能不同,修改前应核对实际配置。
这些端口是请求的目标端口,不是客户端的本地监听端口。例如下面这条规则只将目标端口为 443、域名属于 example.com 的请求交给代理出站,并不覆盖该域名的所有连接。
{
"type": "field",
"domain": ["domain:example.com"],
"port": "443",
"outboundTag": "proxy"
}
- 同一字段内通常是“或”:
domain数组中任意一个条目命中,即满足域名条件。 - 不同字段之间是“且”:同时填写
domain和port,必须同时满足两者。 - 规则之间按顺序尝试:遇到第一条完整匹配的规则后选定出站,不再继续寻找更精确的规则。
二、domain、full、regexp:域名匹配的边界
需要覆盖主域名和全部子域名时,用 domain:;只允许某个完整主机名时,用 full:。不要因为规则名都含有“域名”,就把两者视为相同。域名字段里填写的是主机名,不是网页地址,不应带协议、路径或查询参数。
| 条目写法 | 匹配范围 | 容易混淆的边界 |
|---|---|---|
domain:example.com | example.com 及其子域名,例如 api.example.com | 不匹配 notexample.com,也不匹配 example.com.test |
full:example.com | 仅 example.com | 不匹配 www.example.com |
example | 域名中包含该字符串 | 可能命中 unrelated-example.test,不适合精确站点例外 |
regexp:^api[0-9]+\.example\.com$ | 例如 api1.example.com、api25.example.com | 需要首尾锚点;写入 JSON 时反斜杠还要转义 |
正则表达式只在确有模式需求时使用。下例在 JSON 字符串中将反斜杠写成两次,因此内核收到的正则才包含用于匹配字面圆点的 \.。如果界面字段接收的是原始规则文本,而不是 JSON 字符串,就不需要这一层 JSON 转义。
{
"type": "field",
"domain": [
"full:login.example.com",
"regexp:^api[0-9]+\\.example\\.com$"
],
"outboundTag": "proxy"
}
结论:单站例外先用 full
只想修正一个登录主机时,先添加完整主机名规则并验证;确认全部子域名都需要同一出口后,再扩大为 domain 规则,避免连带改变静态资源和其他业务的出口。
三、ip、geoip、geosite:地址与域名集合分开写
ip 是 JSON 字段,geoip: 是可放入该字段数组的集合引用;geosite: 则放在 domain 数组内。两类集合依赖内核能够读取的对应数据文件。集合名称及其内容由数据文件决定,不是内核根据名称实时生成的结果。
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
}
{
"type": "field",
"ip": [
"192.0.2.15/32",
"2001:db8::15/128",
"geoip:private",
"geoip:cn"
],
"outboundTag": "direct"
}
上面的 192.0.2.15 与 2001:db8::15 是文档示例地址,实际使用时替换成目标服务的地址。IPv4 的 /32 和 IPv6 的 /128 都表示单个地址;网段前缀越短,覆盖范围通常越大。不要为了解决一个地址的访问问题,直接加入覆盖整片网络的大网段。
- geosite:cn:按数据集中的域名分类匹配,不保证该域名所有解析结果都位于同一区域。
- geoip:cn:按目标 IP 所属的地址集合匹配,不保证该地址承载的网站属于某种域名分类。
- geoip:private:用于匹配数据集中定义的私有及保留地址范围,常见用途是让本地资源直连。
- 集合缺失:若启动日志提示无法加载 geosite 或 geoip,先检查数据文件与条目名称,不要把它当作节点故障处理。
四、匹配优先级:例外规则必须排在宽规则前面
内核不会自动比较“完整域名比域名集合更精确”。规则按 rules 数组从上往下执行。如果第一条已用 domain:example.com 直连,那么后面的 full:api.example.com 代理规则没有机会覆盖它。正确顺序是先放例外,再放覆盖面更大的集合,最后放兜底。
下面是可合并到现有配置的 routing 片段,不是完整客户端配置。前提是现有 outbounds 中已经有 proxy 和 direct 两个标签,并且具备所引用的数据集合。若实际标签不同,应先统一标签,不能只复制规则而忽略出站。
{
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"domain": ["full:api.example.com"],
"outboundTag": "proxy"
},
{
"type": "field",
"domain": ["domain:example.com", "geosite:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"ip": ["geoip:private", "geoip:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
| 测试目标 | 首次命中 | 预期出站 |
|---|---|---|
| api.example.com:443 | 第 1 条完整域名例外 | proxy |
| www.example.com:443 | 第 2 条主域及子域规则 | direct |
| 192.168.1.20:80 | 第 3 条私有地址集合 | direct |
| 未命中上述集合的公网目标 | 第 4 条 TCP/UDP 兜底 | proxy |
表格是配置推演,不是联网测速结果。演示域名不保证运行实际服务,验证网页时应换成已知可用的目标。如果移除最后一条兜底规则,未匹配请求通常使用配置中第一个出站;因此不能脱离完整配置,笼统地说“未命中一定代理”或“未命中一定直连”。
五、domainStrategy:为什么 IP 规则没有生效
请求进入内核时,目标可能是域名,也可能已经是 IP。只有地址的请求,不会因为你添加了域名规则就自动恢复成原始域名。反过来,只有域名的请求,要匹配 ip 条件,通常需要路由解析策略提供地址。这正是 domainStrategy 需要单独检查的原因。
| 策略 | 路由阶段行为 | 配置注意点 |
|---|---|---|
AsIs | 不为路由匹配主动将目标域名解析为 IP | 目标本来就是 IP 时仍可匹配 IP 规则;不等于出站永不解析域名 |
IPIfNonMatch | 域名首轮没有规则匹配后,尝试解析并再次匹配 | 首轮若已命中无条件兜底,就不会触发这次补充解析 |
IPOnDemand | 匹配过程遇到需要 IP 的规则时,尝试解析目标域名 | 可能增加 DNS 查询;应同时考虑解析延迟与 DNS 出口 |
上一节使用 IPOnDemand,是为了让第三条地址集合规则能在最后的兜底之前参与判断。如果直接改成 IPIfNonMatch,域名请求可能先命中第四条宽泛规则,导致预期的地址分流没有发生。这不是 geoip 写法错误,而是解析触发时机不同。
结论:先确定需要哪一种信息
按站点分流先保证请求携带域名;按解析地址分流再选择解析策略。DNS 服务器、DNS 查询如何出站,以及嗅探是否覆盖目标,应分别核对,不能靠切换一个 domainStrategy 同时解决。
- 应用显式连接 HTTP 或 SOCKS 代理并提交主机名时,内核通常可以直接按域名规则判断。
- 应用先自行解析再提交 IP 时,域名信息可能丢失;嗅探只能在支持的流量中尝试识别,不能保证还原所有请求。
- 路由中的
domainStrategy与出站连接阶段的同类解析选项职责不同,修改时要确认所在配置层级。
六、在 v2rayN 中应用与验证
普通使用者优先通过路由设置界面维护规则。v2rayN 会生成运行配置,直接修改临时生成的 JSON,可能在重启服务或切换节点后被覆盖。下面以带有「设置」→「路由设置」入口的桌面界面为例;不同界面版本的按钮位置可能变化,但规则顺序和出站映射仍需逐项检查。
-
保留原配置
打开「设置」→「路由设置」,记录当前使用的规则集和域名解析策略。修改前备份已有配置;不要直接清空默认规则集。
-
添加单条规则
在目标规则集中添加一个完整域名条件,例如
full:api.example.com,选择代理出站。界面域名输入框只填写规则条目,不粘贴整个 routing 对象。 -
调整规则顺序
把这条例外移动到对应的
domain:、geosite:或其他宽泛规则之前。保存后确认主界面当前选中的就是刚修改的规则集。 -
重新建立连接
重启服务或重新加载配置,再关闭目标网页的旧连接并重新访问。已有连接一般不会因为规则改动而自动迁移到另一出站。
-
核对运行日志
查看核心日志中的目标主机、目标端口及出站标签。先用一个已知可用的网站验证,再扩大规则范围;日志级别不足时,临时提高到 info 或 debug,完成后恢复。
验证时至少做三组请求:例外主机、同主域下的普通主机、局域网资源。前两组检查域名规则的边界,第三组检查私有地址直连是否被前面的宽泛代理规则遮挡。日志未出现请求时,先检查应用是否经过当前代理入口,而不是继续修改路由表达式。
七、常见规则冲突的快速解答
full 已经写了,为什么还是直连?
先检查它前面是否存在覆盖该域名的 domain:、geosite: 或全流量规则。把完整域名例外移到宽规则之前,保存并用新连接测试;同时核对这条规则选择的出站标签。
把 domain 和 ip 放一起怎么反而不匹配?
它们在一条 field 规则中是同时满足的条件。需要“域名或地址任一命中”时,拆成两条具有相同出站的规则;只有确实需要限定两项条件时才保留合写。
geosite:cn 存在,为什么有些站点仍走代理?
先确认目标实际请求的主机名,例如页面引用的资源域名可能不同。再检查前置规则、数据文件是否加载以及集合是否包含该域名。可以先添加一条明确的 full: 规则验证,并通过访问日志确认实际出站。
设置 port 443 就能匹配所有网页吗?
不能。它只限制目标端口;传统 HTTP 可能使用 80,其他服务也可能使用自定义端口。HTTPS 还可能走 TCP 或 UDP,若额外写了 network: "tcp",就不会匹配 UDP 请求。
订阅更新后,自定义规则还会有效吗?
节点订阅与路由规则属于不同配置内容。更新后检查当前规则集是否仍被选中,以及代理出站是否还指向有效节点。不要把订阅地址当作路由配置,也不要把路由 JSON 粘贴到订阅地址输入框。
排查顺序保持固定:确认请求进入内核,确认域名或地址信息可用,检查第一条命中的规则,再检查出站本身是否能连接。每次只改一类条件,才能分清是规则边界、DNS 策略,还是节点网络造成了结果变化。