Shadowrocket REJECT 策略:屏蔽原理与拦截边界

REJECT 是规则里的三种出站策略之一,命中的连接在本地被拒绝或丢弃,请求不会转发到任何服务器。本文说明它的命中顺序、实际生效范围,以及它对 HTTPS、应用内请求与域名前置的边界。

本文速览

REJECT 只决定一条连接放不放行,不解析传输内容。读完你能分清 REJECT 与 PROXY、DIRECT 在规则列表里的位置关系,知道它为什么拦不住同域名下的推广接口与硬编码 IP 的应用内请求,并按一套固定顺序排查「规则写了却不生效」。适合已经能从自己的服务商处拿到订阅、正在自己整理规则的人。

REJECT 在规则里是什么:一条出站策略

Shadowrocket 的规则行是类型,值,策略的三段式写法,策略段只有三种取值:PROXY(交给当前选中的服务器出站)、DIRECT(本地直连,不进代理)、REJECT(在本地拒绝或丢弃这条连接)。REJECT 不是 Settings 里的一个总开关,它只是某一条规则的动作,只有命中这条规则的连接才会被拦下来。

一条最小可用的屏蔽规则长这样:

DOMAIN-SUFFIX,ads.example.com,REJECT
DOMAIN-KEYWORD,metrics,REJECT
IP-CIDR,203.0.113.0/24,REJECT,no-resolve
FINAL,PROXY

规则写在当前 Config 文件里(Home → 当前配置 → 编辑),也可以在 Home 的规则列表中直接增删。改动后需要重新选中该 Config 才会参与匹配;更新订阅会重新拉取配置文件,本地手改的规则可能被覆盖,更新完建议复查一遍。

3 种
出站策略:PROXY / DIRECT / REJECT
自上而下
规则命中顺序:首条匹配即生效
2 处
规则入口:Config 文件与 Home 规则列表
连接层
REJECT 生效层级:不解析传输内容

命中顺序:REJECT 什么时候才轮得到

每个新连接只做一次规则匹配:从列表第一行开始逐条比对,第一条匹配成功的规则决定这条连接的走向,后面的规则不再参与。REJECT 能不能生效,取决于它前面有没有更宽泛的规则先把这条连接领走。

应用发起请求连接进入隧道规则自上而下匹配首条命中生效连接被本地拒绝

顺序问题出在宽窄关系上。DOMAIN-KEYWORD,ads,REJECT 写在 DOMAIN-SUFFIX,ads.example.com,PROXY 前面,那么 ads.example.com 会先被 keyword 规则拦掉;反过来写,窄规则先命中,后面的 keyword 规则对这条连接就没有机会了。窄规则在前、宽规则在后,是规则列表的基本排法。

DOMAIN,api.example.com,DIRECT
DOMAIN-SUFFIX,example.com,PROXY
DOMAIN-KEYWORD,metrics,REJECT
IP-CIDR,198.51.100.0/24,REJECT,no-resolve
GEOIP,CN,DIRECT
FINAL,PROXY

这六行是一副可用骨架:精确域名 → 域名后缀 → 关键字 → IP 段 → 地理归属 → 兜底,越具体的越靠前。

FINAL 是兜底规则,匹配所有未被前面命中的连接,必须放在最后一行;把它写在中间,等于给它后面的所有规则判了死刑。IP 类规则还有两个细节:带 no-resolve 表示这条规则不触发 DNS 解析,只在目标本身就是 IP 字面量时匹配;不带 no-resolveIP-CIDR 会先把域名解析成 IP 再比对,解析结果不同就可能出现时灵时不灵。

拦截边界:HTTPS、应用内请求与域名前置

REJECT 生效在连接建立阶段:它在本地拒绝或丢弃连接,不参与 TLS 握手之后的数据交换。这决定了它的能力上限——它管的是「这条连接能不能通」,不是「页面里显示什么」。

边界一:HTTPS 只拦到连接层

https://ads.example.com/x.js 这样的请求,REJECT 拦掉的是到 ads.example.com:443 的连接,脚本自然拿不到。但如果推广内容与正文来自同一个域名(例如 example.com 下的一个接口),REJECT 就无能为力:拦掉整个域名,正文也会一起打不开。规则里没有按 URL 路径匹配的关键字,客户端看不到 HTTPS 请求的路径。

边界二:应用内请求与硬编码 IP

很多应用把上报地址写成 IP 字面量,或者从接口动态下发域名。前者域名规则匹配不到,需要 IP-CIDR 兜底;后者要等实际域名出现之后再补规则。按 IP 拦的代价是容易误伤——同一网段上往往还有正常服务,共享 CDN 的 IP 尤其如此。域名规则与 IP 规则是两条互补入口,分别对应下面两种发起方式:

DOMAIN-SUFFIX,ads.example.com,REJECT
IP-CIDR,203.0.113.0/24,REJECT,no-resolve

另一个细节是 QUIC。走 UDP 443 的连接同样会经过规则匹配,但如果你写的是带 no-resolveIP-CIDR,而应用是以域名形式发起连接的,这条规则不会触发解析,也就匹配不到。

边界三:域名前置

域名前置(domain fronting)的做法是:TLS 握手里的 SNI 与请求实际指向的 Host 不一致,SNI 通常是一个「干净」的域名。Shadowrocket 按连接的目标域名(即 SNI)匹配规则,DOMAIN-SUFFIX 拦的是 SNI 上的那个域名。按 SNI 拦会连带影响同一入口上的正常服务;按 Host 拦截则不在客户端规则的处理范围内。

结论:REJECT 是名单式连接拦截,不是内容过滤器

按域名与 IP 匹配、在连接层生效,意味着它能挡住独立域名和独立 IP 上的请求;同域名下的推广接口、URL 路径级的内容、SNI 与 Host 不一致的流量,都不在它的处理范围内。按这个边界设预期,能省掉大量反复改规则的试错。

规则写了却不生效:按这个顺序排查

规则不生效,原因大多落在「当前生效的配置」和「匹配顺序」这两件事上。按下面的顺序逐项确认,比反复重写规则更快。

  1. 确认当前生效的 Config

    回 Home 看顶部显示的配置名。规则写在 A 配置里、当前选中的是 B,规则自然不会参与匹配。

  2. 检查规则在列表中的位置

    看有没有更靠前的宽规则先命中,尤其是 DOMAIN-KEYWORD 排在 DOMAIN-SUFFIX 之前的情况。

  3. 确认连接是域名还是 IP 发起

    域名规则匹配不到 IP 直连;带 no-resolve 的 IP-CIDR 也不会为域名连接触发解析。

  4. 确认 Global Routing 姿态

    停在 Proxy 或 Direct 时,规则列表不参与分流决策;回到 Config 才会按规则逐条匹配。

  5. 更新订阅后复查规则

    订阅更新会重新拉取配置,本地手改的规则可能被覆盖;更新完回 Home 确认规则还在。

注意

REJECT 只作用于经过 Shadowrocket 隧道的连接,也不解析传输内容。规则能改变的是连接走向,不是应用自身的行为。

关于 REJECT 的五个常见问题

加了 REJECT,同一个域名的正常页面也打不开了?

说明这个域名同时承载正文与被拦内容。REJECT 按域名整条拦,不能只拦其中一部分;把规则收窄到具体子域名,或改用 DOMAIN 精确匹配单条主机名。

REJECT 和 DIRECT 到底差在哪?

DIRECT 是放行,连接照常建立,只是不走代理;REJECT 是本地拒绝或丢弃,连接建立不起来。要「能连但不走代理」用 DIRECT,要「连不上」用 REJECT。

规则写在 Home 里,更新订阅后就没了?

订阅更新会重新拉取配置文件,本地手改的规则可能被覆盖。需要长期保留的规则,更新完回 Home 复查一遍,必要时重新加。

按 IP 拦了一整段,结果别的服务也挂了?

IP-CIDR 拦的是整个网段,共享 CDN 的 IP 上往往还有正常服务。优先用域名规则;确实要按 IP 处理时把网段切细,并带上 no-resolve 避免额外解析。

为什么被 REJECT 的连接在 Data 页看不到流量?

连接在建立阶段就结束了,没有实际传输量,按服务器与按应用统计的读数自然不会增长。判断规则是否命中,以 Home 里的当前配置与规则顺序为准。

App Store 正版核验