sing-box 分流没生效?先查规则顺序:首条命中就停,后面的规则再对也没用

sing-box 分流没生效?先查规则顺序:首条命中就停,后面的规则再对也没用

背景:规则写了,分流却走了另一条路

sing-box 是一个开源的通用代理平台,客户端和服务端共用同一份 JSON 配置。配置里的 route 段就是一张分流表:每条规则写着“符合什么条件,走哪个出站(direct 直连、proxy 代理、block 拦截)”。

最常见的排错困境是这样的:你明明给某个域名写了走代理的规则,它却走了直连;或者明明写了直连,流量还是绕了代理。配置能通过 sing-box check 校验,语法没有错——错在顺序。

前置知识:一份能跑起来的 sing-box 客户端配置,知道出站是配置里带 tag 的条目。规则的基本写法见本站的路由规则指南,DNS 分流的坑见代理开了却部分网站打不开。

核心模型:两条规则,缺一不可

第一条:自上而下,首条命中即停。 sing-box 按 route.rules 数组的书写顺序逐条匹配,第一条命中的规则决定去向,后面的规则不再看。顺序就是优先级:特例写前面,大网写后面。

第二条:一条规则内部是“与”,不同规则之间是“或”。 按官方路由规则文档,同一条规则里域名、端口、IP、来源等不同类别的条件要同时满足才算命中;同一类别里写多个值(比如两个域名后缀),满足任一即可。于是“域名 A 且端口 443”要写进同一条规则,而“域名 A 或域名 B”可以写进同一条规则的同一个字段,也可以拆成两条规则——效果一样,但拆开后顺序就有了意义。

兜底是 final。 一条规则都没命中的流量走 route.final 指定的出站;如果 final 为空,默认走第一个出站。很多人分流“莫名其妙”的根源,其实是流量根本没命中任何规则,悄悄走了 final。

五种常见顺序错误

1. 大网写在特例前面,特例永远排不上

这是最典型的一种。先写了一条“某国 IP 段走直连”,后面再写“其中某个域名走代理”:如果该域名的解析结果正好落在那个 IP 段里,第一条就命中了,第二条永远不会被看到。修法只有一个:把特例上移,把大网下移。

2. 域名规则没机会匹配:嗅探没开

域名规则要生效,前提是 sing-box 知道这次连接的域名。走 TUN 入站时,流量起初只有目标 IP,没有域名,必须靠嗅探(sniff)从 TLS 握手里读出域名。如果没开嗅探,域名规则一条都匹配不上,流量按 IP 规则或 final 走,看起来就像“域名规则失效”。修法:在 rules 最前面放一条嗅探规则:

{ "action": "sniff" }

注意嗅探规则本身不决定去向,它只是让后面的域名规则有机会工作。混合入站(mixed)下浏览器本来就会把域名交给代理,嗅探的影响相对小;TUN 场景下它几乎是必备项。

3. 规则集写了名字,却没定义来源

rule_set(自 1.8.0 起的规则集写法,geosite/geoip 字段已废弃)必须在 route.rule_set 里另外声明:是本地文件还是远程下载、下载走哪个出站。只在规则里写个名字、没在 rule_set 里定义,规则不会生效,流量滑向 final。远程规则集下载本身也要走对出站,否则下载失败同样等于规则不存在。

4. 同一条规则里混了“与”和“或”的想象

想表达“域名 A 走代理,或者端口 443 走代理”,却把域名和端口写进同一条规则——按“与”逻辑,这条规则要求两者同时满足,单独的域名 A(非 443 端口)或单独的 443 端口(非域名 A)都命中不了。需要“或”时拆成两条规则;需要“与”时才合在一条里。

5. final 设错,等于全盘默认错

把 final 设成直连,意味着所有没写规则的流量默认直连;设成代理,则默认全走代理。排错时先确认:出问题的流量到底是命中了某条规则,还是根本没命中、走了 final。这两者的修法完全不同:前者调顺序,后者补规则或改 final。

一个推荐的顺序模板

按“从特殊到一般”排列,基本不会踩坑:

"route": {
  "rules": [
    { "action": "sniff" },
    { "ip_is_private": true, "outbound": "direct" },
    { "rule_set": "ads-block", "outbound": "block" },
    { "rule_set": "my-special-sites", "outbound": "proxy" },
    { "rule_set": "geosite-cn", "outbound": "direct" },
    { "rule_set": "geoip-cn", "outbound": "direct" }
  ],
  "final": "proxy"
}

读法是:先嗅探,再放行局域网,再拦截广告,然后是自己指定的特例站点,接着是国内域名和国内 IP 直连,最后其余全部走代理。注意上面用的是泛称 tag(proxy、direct、block 需换成你配置里真实的出站名),规则集也需要在 route.rule_set 里定义来源。把出站分组做好的读者,可以对照本站的出站分组实战,让 proxy 指向自动测速分组。

四步定位法:到底是哪条规则截胡了

第一步:校验语法。 改完先跑 sing-box check -c config.json,确认没有拼错的 tag 或字段。能通过校验只说明格式对,不说明顺序对。

第二步:二分法移规则。 把怀疑的那条规则临时移到 rules 最前面(嗅探规则之后),重启客户端再测。如果这时分流对了,说明前面确实有某条更宽的规则抢先命中;再把它一步步往下挪,直到刚好出问题的位置,就是截胡的那条。

第三步:用拦截做探针。 把怀疑的规则出站临时改成 block,访问目标网站:如果打不开了,说明流量确实走了这条规则;如果还能打开,说明这条规则根本没命中,问题在它之前或在 final。测完记得改回来。

第四步:分开查 DNS。 如果域名规则和 IP 规则给出的结论互相矛盾——域名看着该走代理、解析到的 IP 却落在直连段里——先按上一篇 DNS 分流排错检查两路 DNS:解析走错路时,IP 规则会先于域名规则把流量定下来。

排错清单

  • 特例在前、大网在后,顺序即优先级;
  • TUN 场景先放嗅探规则,否则域名规则无从匹配;
  • 规则集要在 route.rule_set 里定义来源和下载出站;
  • 同一规则内是“与”,跨规则是“或”,别把两者写反;
  • 先区分“命中了错规则”还是“没命中、走了 final”,再动手改。

分流问题十有八九不是规则写错了字,而是规则站错了队。顺序理顺了,大多数“没生效”都会自己消失。