[Rule] / [Proxy Group] / [Host] 三段的完整语法,外加模块能改的那部分 DNS 设置。

[Rule] 行语法

类型,匹配值,出口
类型,匹配值,出口,选项
FINAL,出口
AND,((类型,值),(类型,值)),出口
RULE-SET,https://example.com/list.txt,出口

字段按逗号切分,每个字段前后的空格会被去掉。第三个字段是出口,它后面的都当作选项。

匹配顺序是从上到下,第一条命中的规则决定出口,后面的不再看。 所以精确规则写在前、宽泛规则写在后。

一行只写一个匹配值,不能写 DOMAIN-SUFFIX,a.com,b.com,PROXY。多个值写多行。

行尾不能写注释,,DIRECT # 说明 会把 DIRECT # 说明 整个当成出口名。注释写在上一行。

规则类型全表

类型 匹配对象 取值写法 例子
DOMAIN 完整域名,精确相等 域名,不带协议和路径 DOMAIN,www.google.com,默认
DOMAIN-SUFFIX 域名后缀 不带前导点。google.com 同时命中 google.com 和 a.google.com DOMAIN-SUFFIX,google.com,默认
DOMAIN-KEYWORD 域名里含某段字符 任意子串 DOMAIN-KEYWORD,google,默认
DOMAIN-REGEX 域名正则 RE2 正则,建议加 ^ $ 锚定 DOMAIN-REGEX,^.*\.google\.com$,默认
DOMAIN-WILDCARD 域名通配 * 任意长度,? 单字符 DOMAIN-WILDCARD,*.google.com,默认
IP-CIDR 目标 IPv4 网段 带掩码,单个 IP 写 /32 IP-CIDR,149.154.160.0/20,默认
IP-CIDR6 目标 IPv6 网段 带掩码,单个 IP 写 /128 IP-CIDR6,2001:b28:f23d::/48,默认
IP6-CIDR 同 IP-CIDR6
SRC-IP-CIDR 来源 IP 网段 带掩码 SRC-IP-CIDR,192.168.1.0/24,DIRECT
GEOIP 目标 IP 归属地 规则集名,见下文 GEOIP,cn,DIRECT
GEOSITE 域名所属集合 规则集名,见下文 GEOSITE,category-ads-all,REJECT
DST-PORT 目标端口 单个端口号 DST-PORT,443,默认
PORT 同 DST-PORT
SRC-PORT 来源端口 单个端口号 SRC-PORT,1080,DIRECT
PROCESS-NAME 发起连接的进程 可执行文件名 PROCESS-NAME,Google Chrome,默认
PROTOCOL 传输层/应用层协议 见下表 PROTOCOL,UDP,REJECT

补充:

  • 域名类的匹配值必须自己写成小写。 客户端只把被匹配到的域名转小写,规则里的值原样使用 —— 写了 DOMAIN-SUFFIX,Google.COM 就永远不命中,而且没有任何报错。GEOIP / GEOSITE 的名字例外,它们会被自动转小写。
  • IP-CIDR 和 IP-CIDR6 内部是一回事,写对应的那个只是为了可读。裸 IP 不带掩码也能认,但写全更清楚。
  • IP 配置不是合法地址或网段时,内核将无法启动,不会只跳过该规则。DOMAIN-REGEX 使用无效正则表达式时也会导致相同结果。
  • 端口不支持区间,DST-PORT,80-443 会让整条规则失效。多个端口写多行。
  • PROCESS-NAME 只在 macOS 和 Windows 桌面端生效。手机端没有进程归属能力,写了不会命中。macOS 填 .app 里的可执行文件名(一个应用常有主程序 + 若干 Helper,都要写),Windows 填 chrome.exe 这样的文件名。

PROTOCOL 的取值

值 含义
TCP TCP 连接
UDP UDP 连接
QUIC QUIC / HTTP/3
HTTP 明文 HTTP
TLS TLS 连接
HTTPS 同 TLS

其它值不认,整条规则被丢弃。

明确不支持的类型

以下类型不受支持。包含这些类型的规则会被忽略,且不会显示错误或提示:

USER-AGENT、URL-REGEX、IP-ASN、DOMAIN-SET、DEST-PORT-RANGE、SUBNET、DEVICE-NAME、SCRIPT、CELLULAR-RADIO、RULE-SET-IP

上表之外的任何类型同样被跳过,同样没有提示。导入界面不会告诉你「有 N 条被跳过」,所以只用规则类型全表里列出的类型。

要按 URL 路径而不是域名来拦截,用 [URL Rewrite] 而不是 URL-REGEX。

选项

第三个字段之后的都是选项。只有 no-resolve 会被识别(大小写不敏感),写在 IP 类规则上表示不要为了匹配它去做 DNS 解析:

IP-CIDR,91.108.4.0/22,默认,no-resolve

其它选项(force-remote-dns、extended-matching、pre-matching 等)被忽略,写了不报错。

FINAL

FINAL,默认

FINAL 指定所有其他规则均未命中时使用的出口,等价于 Clash 风格的 MATCH,默认。配置多条时不会报错,最后一条生效。FINAL,REJECT 以及其他 REJECT 变体会被忽略,默认规则保持不变。

FINAL 会替换客户端的默认规则。只有需要控制所有未匹配流量时才应配置它;仅修改少量域名出口的模块不需要 FINAL。

AND / OR

多个条件组合:

AND,((DOMAIN-SUFFIX,googlevideo.com),(PROTOCOL,UDP)),REJECT
OR,((DOMAIN-SUFFIX,youtube.com),(DOMAIN-SUFFIX,youtu.be)),默认
  • 子条件写在双层括号里,每个子条件用一层括号包住,子条件之间用逗号分隔。
  • 子条件不包含出口,出口只写在最外层。子条件中多余的第三个字段会被当作选项忽略,且不会显示错误。
  • 子条件里可以带 no-resolve。
  • 子条件里任何一个类型不被支持,整条规则都会被丢弃(不会退化成部分匹配)。
  • 没有 NOT。

RULE-SET

引用一份外部规则列表:

RULE-SET,https://example.com/reject.list,REJECT
  • 值必须是 http:// 或 https:// 开头的完整链接。
  • 列表内容是每行 类型,值 的纯规则(没有出口列),整份列表统一走这条写的出口。列表里能用的类型比 [Rule] 少,见 modules.md 的「独立规则列表」。
  • 不支持裸名字,RULE-SET,SYSTEM,DIRECT、RULE-SET,LAN,DIRECT 或者引用别处定义的具名 provider 都会被跳过。
  • 外部规则集的优先级高于同一模块 [Rule] 段里手写的规则。

Loon 风格的 [Remote Rule] 段写法也支持:

[Remote Rule]
https://example.com/reject.list, policy=REJECT, tag=广告, enabled=true

policy 必填;tag 不填时取 policy;enabled 不填按 true。

[Remote Filter]

Loon 风格的具名筛选分组,等价于一个「用正则从全部订阅节点里筛成员」的手动分组:

[Remote Filter]
香港 = NameRegex, FilterKey="(?i)(香港|HK)"

只有 FilterKey 被当成筛选正则;第一个字段(NameRegex / NameKeyword 等)写什么都按同一种处理。自己写模块用 [Proxy Group] 就行,这一段只是为了能直接导入 Loon 的模块。

出口怎么写

规则的第三个字段、以及分组的成员,都是「出口」。解析顺序:

  1. REJECT 开头的名字 → 拦截,见下。
  2. 任何已启用模块里 [Proxy Group] 定义过的分组名,以及客户端配置里已有的出口名 → 该出口。跨模块有效:A 模块的规则可以指向 B 模块建的分组。
  3. DIRECT(大小写不敏感)→ 直连。
  4. 其他名称 → 使用客户端的「默认」出口。

第 2 条里的「客户端已有的出口名」包括三个内置名,规则里可以直接写:

名字 含义
默认 主选择器,也就是用户在节点页里选的那个。等于「走代理」
自动选择 按延迟自动挑最快的节点
直连 不走代理,与 DIRECT 等价

订阅自带的分组名(节点页里能看到的那些)同样可以直接写。

PROXY 可以使用,但它不是关键字。 PROXY 未被定义时会按第 4 条使用「默认」出口,结果与直接写 默认 相同。其他未定义或拼写错误的分组名也按此规则处理,且不会显示错误。建议直接使用 默认,以明确表达配置含义。

结论:走代理写 默认(写 PROXY 也行,只是配置本身看不出对错),直连写 直连 或 DIRECT;要精确控制走哪个地区,自己在 [Proxy Group] 里建一个分组,再在规则里写这个分组名。

REJECT 家族

写法 效果
REJECT 断开连接
REJECT-NO-DROP 断开连接,不静默丢弃
REJECT-DROP 静默丢弃,不回任何东西
REJECT-200 回一个空的 200
REJECT-IMG 回一张 1×1 PNG
REJECT-TINYGIF 回一张 1×1 GIF
REJECT-DICT 回 200 {}
REJECT-ARRAY 回 200 []
REJECT-VIDEO 回一段空 mp4

后四种和 REJECT-200、REJECT-IMG、REJECT-TINYGIF 属于内容型拦截:要伪造一个 HTTP 响应,必须开着 HTTPS 解密,而且规则本身必须是 DOMAIN / DOMAIN-SUFFIX / DOMAIN-KEYWORD 三种之一。

不满足条件时(未启用解密,或者规则类型为 GEOSITE / IP-CIDR / DOMAIN-REGEX / PROCESS-NAME / AND 等),会按普通 REJECT 处理。目标仍会被拦截,但结果从返回模拟响应变为断开连接。应用可能表现为空白页面或连接错误。

REJECT、REJECT-IMG、REJECT-TINYGIF、REJECT-DICT、REJECT-ARRAY、REJECT-VIDEO 可以带 -NO-DROP 后缀。除此之外的 REJECT-* 变体(包括 REJECT-200-NO-DROP)不受支持,并按普通 REJECT 处理。不确定时请使用不带后缀的形式。

[Rule] 段的 -NO-DROP 规则和 [URL Rewrite] 段的 -no-drop 规则不一样,后者见 mitm.md。

[Proxy Group]

分组名 = 类型, 成员1, 成员2, key=value, ...

分组类型

写法 行为
select / static 手动选,用户在客户端里点
url-test / url-latency-benchmark 定时测速,自动用最快的
fallback / available 按顺序取第一个可用的
load-balance / round-robin / balance 在成员间轮流分配

无法识别的类型会使整行配置被忽略,包括 ssid、subnet 等类型。

参数

key 作用
url 测速用的探测地址。只对 url-test / fallback / load-balance 有意义
interval 测速间隔,秒。常用 300
policy-regex-filter 用正则从全部订阅节点里筛成员。别名:filter、include-regex、FilterKey
exclude-regex 从筛出来的结果里再排除掉匹配的
hidden 写 hidden 或 hidden=1,这个分组不在客户端列表里显示

其它 key(tolerance、no-alert、include-all、timeout 等)被忽略,写了不报错。

只要写了 policy-regex-filter 或 exclude-regex,分组就自动包含全部订阅节点,不需要手写成员名。这是推荐写法:节点名变了不用改模块。

正则筛选和手写成员可以混用,例如在 url-test 分组外增加一个 fallback 分组。

例子

[Proxy Group]
香港 = url-test, policy-regex-filter=(?i)(香港|hong ?kong|HK|🇭🇰), url=http://www.gstatic.com/generate_204, interval=300
新加坡 = url-test, policy-regex-filter=(?i)(新加坡|singapore|SG|🇸🇬), exclude-regex=(?i)(试用|过期), url=http://www.gstatic.com/generate_204, interval=300
流媒体 = select, 香港, 新加坡, DIRECT
香港兜底 = fallback, 香港, 默认, url=http://www.gstatic.com/generate_204, interval=300, hidden=1

流媒体 这类手动分组的成员可以是其他分组名、DIRECT、客户端内置出口名或具体节点名。hidden=1 可隐藏仅供其他分组引用的内部分组。

正则的限制

分组筛选用的正则是 RE2,和 JavaScript 的正则不一样:

  • 不支持环视:(?=...)、(?!...)、(?<=...)、(?<!...) 全部不行。
  • 不支持反向引用:\1、\2 不行。
  • 支持行首 (?i) 表示忽略大小写。
  • 支持 | 分支、{2,3} 量词、字符类、\d \w \s 等常用写法。

使用不受支持的语法会导致内核无法启动,通常表现为节点列表为空且无法连接。这是节点列表突然为空的常见原因。需要排除节点时应使用 exclude-regex,不要使用环视。

引号

分组定义的尾部按逗号切分,但双引号里的逗号不切。所以正则里带 {2,3} 这类量词时,整个值要加引号:

测试 = url-test, policy-regex-filter="(?i)节点[0-9]{2,3}", interval=300

命名禁忌

分组名不能与已有出口重名。 发生重名时,新分组会被忽略且不会显示错误;规则仍指向原有同名出口。内核可正常运行,但新分组不会出现。

请勿使用:默认、自动选择、直连、订阅自带分组名,以及其他已启用模块定义的分组名。重复安装同一模块时,后一份同名分组也会被忽略,但不会影响内核启动。

另外两类名称会改变解析结果:

  • 分组名以 REJECT 开头(REJECT广告 之类):出口解析第 1 步就把它当成拦截策略,这个分组永远解析不到。
  • 分组名叫 DIRECT:出口解析第 2 步早于第 3 步,于是所有写 DIRECT 的规则都指向这个分组,而不是真直连。

成员的处理

  • 成员里的 REJECT 会被丢掉(分组不能选「拦截」)。
  • 成员里的 DIRECT 变成直连。
  • 无法识别的成员名使用「默认」出口;多个无法识别的成员会合并为同一项。因此建议使用正则筛选,避免依赖未经验证的成员名。
  • 分组不能为空。 未配置成员或正则筛选(我的组 = select),或者所有成员均被忽略(广告 = select, REJECT),都会导致内核无法启动。

[Host]

域名的解析方式,优先于客户端自己的 DNS 规则。两种写法:

[Host]
# 1. 静态映射:直接回这个 IP,不问任何 DNS
example.com = 1.2.3.4
internal.corp = 10.0.0.5
dual.example.com = 1.2.3.4, 2001:db8::1
*.lab.example.com = 10.0.0.9

# 2. 指定解析器:仍然去查,只是换一台 DNS 问
*.corp.example.com = server:10.0.0.53
*.intranet.example.net = server:10.0.0.53
  • 静态映射的值只能是 IP 字面量。一行可以写多个,逗号分隔,IPv4 / IPv6 混写。

  • *.x.com 匹配任意子域。

  • 同一行两种混写时,静态映射优先,server: 部分忽略。

  • 指定解析器的地址也只能是 IP(可带端口 10.0.0.53:5353,可写 tls:// https:// quic:// 前缀),或者 server:system 表示交给系统解析器。 写域名的话这条被丢掉。

  • 走这台 DNS 的域名不会被分配虚拟 IP,拿到的是真实应答 —— 内网域名解析出内网地址, 直接可用。

  • 用它指到的 DNS 一律直连访问,不经过节点。

  • 保留值 server:fakeip(也可写 fake-ip)会为指定域名分配虚拟 IP,不在本地执行真实 解析,并由 [Rule] 决定出口。客户端内置规则会将部分域名判定为直连。如需让其中某个 域名使用代理出口,必须同时配置 [Host] 和 [Rule]。仅配置 [Rule] 时,DNS 可能 已返回中国大陆的真实 IP,随后流量仍会经代理连接该地址,产生不必要的额外路径:

    [Host]
    weather-map2.apple.com = server:fakeip
    
    [Rule]
    DOMAIN,weather-map2.apple.com,PROXY
    

    它不产生任何 DNS 查询(虚拟 IP 是本地生成的),真正的解析发生在出站一侧。

DNS

[General] 的 dns-server

模块可以改直连域名用哪台 DNS 去解析 —— 换成公司 / 校园内网的解析器,内网的 split-horizon 域名才解析得对。

[General]
dns-server = 10.0.0.53, system
  • 此处配置的解析器排在客户端原有解析顺序的最前面。查询失败时会继续使用原有解析器, 因此切换网络后仍可继续解析。
  • 地址规则同 [Host] 的 server::只收 IP(可带端口和 tls:// https:// 前缀) 或 system。
  • [General] 段只认 dns-server 这一个键,其余键(代理端口、绕过列表、日志级别……) 照旧忽略。

不受模块控制的部分

节点和入口域名的解析不受模块影响,始终使用客户端内置的解析路径。以上配置只作用于 用户访问的目标域名。其他 DNS 行为,包括缓存、虚拟 IP 模式和区域分流判定,也由客户端控制。

其它格式的等价写法

从 Clash / Stash(.stoverride)或 Quantumult X(.snippet)导入时,下面这些字段 会被读取,语义与上面两节相同:

来源字段 等价于
dns.nameserver-policy [Host] 的 server: 写法
dns.direct-nameserver [General] dns-server
dns.fake-ip-filter / fake-ip-filter+ 这批域名不分配虚拟 IP,拿真实应答
dns.nameserver-policy 的值写 fakeip [Host] 的 server:fakeip,强制分配虚拟 IP
QX [dns] 的 server=/域名/IP [Host] 的 server: 写法
QX [dns] 的 server=IP [General] dns-server

dns: 段里的其它键(enhanced-mode、respect-rules、nameserver、fallback 等) 一律忽略。域名模式支持 +.a.com、*.a.com、.a.com、a.com 四种写法; 裸通配(单独一个 * 或 +.)不接受,那等于把所有解析都交出去;通配只能在开头, +.stun.*.* 或 a.*.com 这种中间带星的也不接受 —— 匹配的是字面后缀,星号在中间会被 当成一个真的星号字符去比,规则永远不命中。

写法不被接受的 DNS 设置(域名模式表达不了、解析器地址写成了域名、[Host] 那行一个 IP 都没有)会在导入时提示,模块详情页的「需注意的规则」里也能逐条看到。模块其余部分照常生效, 但这几条一次都不会生效。

GeoIP / GeoSite

GEOIP,cn 和 GEOSITE,google 会转成对公开规则集目录的引用:

https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/sing/geo/geoip/<名字>.srs
https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/sing/geo/geosite/<名字>.srs

名字会被转成小写。带属性的写法(google@cn)和带否定的写法(geolocation-!cn)也是目录里的文件名,直接写即可。

常用的 GeoSite 名字:cn、google、youtube、netflix、telegram、openai、apple、microsoft、category-ads-all、geolocation-!cn。 常用的 GeoIP 名字:cn、private、telegram、google、netflix、cloudflare。

要用别的名字,先去上面那个目录确认文件存在。

名字写错不会报错

规则集在运行时下载。名称错误会导致下载返回 404,相应规则不会命中;内核仍会正常运行,且不会显示配置错误。可通过模块详情页的规则数量确认下载结果。

客户端会在导入模块时检查每个 geo 名称是否存在,无法确认的名称会显示提示。出现提示时应更正名称。

模块详情页会显示每个规则集下载的规则数量;显示 0 条表示下载未成功。