规则

一份有序的名单。先命中的一行说了算。

规则文件只在全局策略为 Config 时被询问。Proxy 与 Direct 会跳过整份文件。首页的试纸用一份示意名单匹配域名,和设备上正在用的文件不是同一份。

  1. 01何时运行
  2. 02一行三个部分
  3. 03匹配顺序
  4. 04域名条件
  5. 05地址与地理库
  6. 06DIRECT · PROXY · REJECT
  7. 07主机名从哪来
  8. 08三条请求的走读
  9. 09多份配置与外来列表

01

引擎的开关在策略上,不在文件里

可以写得再完整的规则,只要策略停在 Proxy,出站仍一律进当前记录;停在 Direct,则一律原路径。文件不会「自己打开」。把策略改回 Config 之后,当前选中的那一份配置才开始逐条匹配。策略三项的含义见 隧道

配置文件可以有多份。连接时只使用当前选中的一份。服务器记录是另一套对象:换高亮行不会换文件,换文件也不会换高亮行。一份配置里写了什么名单,与列表里此刻高亮哪一条出口,是两件独立的事。见 对象配置

因此「规则没生效」有两种完全不同的意思。一种是引擎根本没被问到(策略不是 Config,或选中的不是你以为的那份文件)。另一种是引擎问到了,但先命中的那一行不是你以为的那一行。对照时先确认前者,再改后者。

首页试纸不是设备上的文件

产品页上的域名试纸用一份写死的示意名单。它只演示「自上而下、先命中的一行生效」。GEOIP 在网页上模拟不了。把试纸的结果当成自己手机上的分流,会得到错误结论。

02

一行三个部分

常见写法是:条件类型,参数,动作。用逗号分隔。类型与参数决定「这行管谁」,动作决定「管到了之后怎么办」。行首以 # 开头的是注释,引擎跳过,不会参与匹配。

# 注释不会被匹配
DOMAIN,api.example.com,PROXY
DOMAIN-SUFFIX,example.com,DIRECT
IP-CIDR,10.0.0.0/8,DIRECT
GEOIP,CN,DIRECT
FINAL,PROXY

动作只有三种会真正改变出站:DIRECT(原路径)、PROXY(当前高亮的记录)、REJECT(拦截,不发出)。有的文件会写成策略组名,那是把动作指向另一套选择逻辑;本页只讨论这三种直接动作。类型选错、逗号写少、把 URI 整段贴进规则行,这一行通常不会按你期望工作,请求会继续往下掉。

同一文件里可以混用域名条件与地址条件。引擎对每一条连接只走一遍名单,不会「域名命中一次、IP 再命中一次然后取并集」。先命中的那一行已经给出动作,后面的行不再看。

03

更窄的条件写在更宽的前面

Shadowrocket 配置文件 default.conf:Rule 289 条
配置文件里的 Rule。规则只在策略为 Config 时被问到。截自 App Store,以你设备上的版本为准。

匹配自上而下。某一行命中之后,后面的行不再看。所以例外必须写在通例之前。把 FINAL 放在文件中间,等于下面所有行作废——它们还在文件里,但永远不会被问到。

下面两份名单的行完全相同,只是顺序对调,结果相反。

# A:先点名主机,再写后缀
DOMAIN,api.example.com,PROXY
DOMAIN-SUFFIX,example.com,DIRECT
FINAL,PROXY

# B:先写后缀,api 那一行永远走不到
DOMAIN-SUFFIX,example.com,DIRECT
DOMAIN,api.example.com,PROXY
FINAL,PROXY

api.example.com:名单 A 命中第一行,走记录;名单 B 命中后缀,走直连。对 www.example.com:两份都会在后缀处走直连。对 unrelated.org:两份都落到 FINAL,走记录。

KEYWORD 比 SUFFIX 更宽,GEOIP 比大多数域名行更宽,FINAL 最宽。一个稳妥的顺序是:具体主机 → 后缀 / 关键字 → 网段 → GEOIP → FINAL。这不是语法要求,是为了让例外有机会被看见。

04

域名条件:谁命中谁

DOMAIN
主机名必须完全相等。DOMAIN,example.com,PROXY 匹配 example.com,不匹配 www.example.com,也不匹配 api.example.com。需要管一个精确名字时用它,不要用它冒充后缀。
DOMAIN-SUFFIX
主机名等于该后缀,或以「.后缀」结尾。apple.com 匹配 apple.com 与 www.apple.com、icloud.com 不在其内。不匹配 notapple.com:后者只是以 apple.com 这串字符结尾,中间没有那一个点。
DOMAIN-KEYWORD
主机名包含该字符串即命中。KEYWORD,google 会打到 google.com,也会打到 googlevideo.com、以及任何名字里夹了 google 的主机。范围最大,也最容易误伤。能改成后缀时不要用它。

后缀行不会自动覆盖子域的子域以外的名字。写了 DOMAIN-SUFFIX,google.com,PROXY 并不等于管到了 gstatic.com、youtube.com、googleapis.com。那些是不同的注册域,要各自写,或接受它们落到更后面的 GEOIP / FINAL。

大小写按域名惯例视为不敏感。国际域名若以 Unicode 出现在规则里、实际请求却是 punycode(xn--),两边对不上,这一行不会命中。对照时以规则文件里实际写下的字节为准。

05

地址、端口、地理库

IP-CIDR
按目的 IPv4 网段。例如 10.0.0.0/8172.16.0.0/12192.168.0.0/16 常被写成 DIRECT,避免局域网进出隧道。只写这一条时,IPv6 目的地址不会命中,请求继续往下掉。
IP-CIDR6
IPv6 网段。需要同时处理两种地址族时,两条都要写,或在设置里关掉不使用的那一种。只补了 IPv4 的私网段、设备却用 IPv6 访问同一服务,看起来会像「规则有时灵有时不灵」。
GEOIP
按目的 IP 在地理库中的归类,不是按域名注册地,也不是按网站语言。GEOIP,CN,DIRECT 的意思是:这个目的地址被库标成 CN 时走直连。库版本在设备上,网页试纸模拟不了。CDN 把同一主机名指到不同国家的地址时,GEOIP 的结果会跟着变。
DEST-PORT
按目的端口。用来把某一类出口(例如只想让 443 进记录)从其它端口里分开。它看不到主机名;和域名行写在一起时,仍然是谁先命中谁说了算。
USER-AGENT
按客户端声明的 UA 字符串。只有请求里带得出这项时才有意义。许多应用不走 HTTP 头,或把 UA 留空,这一行对他们等于不存在。
FINAL
应放在最后。前面谁都没命中时用这里的动作。不要省略,也不要依赖「不写就默认 PROXY」这种猜测。FINAL 写成 PROXY 还是 DIRECT,决定了所有未被点名流量的命运,比再加一百条域名行更关键。

部分 IP 行带 no-resolve 一类标注,表示不要为了匹配这一行去把域名解析成地址。解析策略还会和 解析 页里的 DNS 设置互相影响:规则若只能看见主机名、看不见地址,地址行全部落空;只能看见地址、看不见主机名,则域名行全部落空。

06

三种动作不是「连上」的不同程度

把本该直连的系统服务写成 PROXY,常见后果是推送、照片同步、商店更新变慢或失败,因为它们被送进了一条并不为它们准备的出口。把本该进记录的主机写成 DIRECT,则 Data 只见 Direct 增加,网页表现得像「没开代理」。把主机写成 REJECT,应用会报网络错误,容易被误判成节点坏了。

改动作之前先确认这一行真的会被命中。改了一行从未被走到的规则,屏幕上不会有任何变化。用 读数 看计数站在哪一侧,比反复开关更有效。

07

规则看到的名字,不一定是地址栏里那串

域名条件依据解析结果、HTTP 的 Host、或 TLS 的 Server Name Indication。应用如果直接连接 IP、或不带 SNI,规则可能只能看到地址,于是域名行全部落空,最后打在 IP-CIDR、GEOIP 或 FINAL 上。

这就是「同一网站,有时走代理有时走直连」的一种来源:有的请求带主机名,有的只剩 IP。浏览器地址栏显示 www.example.com,底层却可能先连 CDN 的地址;GEOIP 看到的是那台 CDN 的所在库,不是你对这个名字的印象。

系统 DNS 与应用内 DNS 各设一套且互相矛盾时,规则看到的主机名和真实目的地址会对不上。解析没有异常时,不必同时改两处。详见 解析

改写 发生在请求已经被看见之后,不能代替规则决定走哪条路。先分流,再改写。HTTPS 内容级的改写还要额外的解密授权,见 系统

08

用同一份名单走读三条请求

假设当前策略为 Config,文件如下,当前高亮一条可用的记录:

DOMAIN-SUFFIX,apple.com,DIRECT
DOMAIN-SUFFIX,icloud.com,DIRECT
DOMAIN-SUFFIX,github.com,PROXY
IP-CIDR,10.0.0.0/8,DIRECT
GEOIP,CN,DIRECT
FINAL,PROXY

www.apple.com

主机名以 .apple.com 结尾,命中第一行,DIRECT。不会问 github,也不会问 GEOIP。Data 应见 Direct 增加。这与「Apple 在不在中国」无关,只与这一行写成了什么有关。

github.com

前两行不匹配,第三行命中,PROXY。GEOIP 写在后面,即使这个地址被库标成某个国家,也不会再被问到。Data 应见 Proxy 增加。若这里 Proxy 不动、网页失败,先查记录而不是再加一条 github 规则。

只知道目的地址 10.1.2.3,没有主机名

所有域名行落空,命中 IP-CIDR,DIRECT。若同一台服务有时用名字、有时用地址,你会看到两条完全不同的动作。这不是引擎随机,是两次请求带给引擎的信息不一样。

一个未被点名、目的 IP 被库标成 CN 的主机

域名行都不中,网段不中,GEOIP,CN 命中,DIRECT。若库把这个地址标成别的国家,则落到 FINAL,PROXY。换一次解析、换一个 CDN 节点,同一名字可以在这两行之间跳动。

09

多份配置,外来列表

应用允许保存多份配置,连接时只跑当前选中的一份。从一份切到另一份,等于整份名单替换,不是「两条 FINAL 叠在一起」。导入别人的列表之前,先找到它的 FINAL 和 GEOIP:这两行决定了大多数未点名流量的命运。

若你的记录本身在国内、只想让少数主机走记录,FINAL 写成 PROXY 会把大量无关请求送进节点。若记录在境外、希望未点名的都走记录,FINAL 写成 DIRECT 则多数网站根本不会进节点。外来列表的作者按他自己的出口在写,不是按你的出口在写。

列表会过期。订阅型规则的刷新与服务器容器的刷新是不同对象上的同一类动作:保存的是 URL,拉取成功才替换本地文件,失败时保留上一次成功的文件,不会为了失败把规则抹空。刷新走当时的网络路径,开关正连着一条已失效的记录时,规则订阅本身也可能刷新失败。见 对象配置

一次只改一个条件。改完用固定的一条记录、只切换策略做对照,见 读数。同时换记录、换文件、改 FINAL,屏幕上的变化无法归因。