模块是一份纯文本。开头几行是元数据,之后按 [段名] 分段。
最小例子
#!name=YouTube 去广告
#!desc=拦截 YouTube 的广告接口与播放埋点。需开启 HTTPS 解密。
[Rule]
AND,((DOMAIN-SUFFIX,googlevideo.com),(PROTOCOL,UDP)),REJECT
[URL Rewrite]
^https?:\/\/(?:www|s)\.youtube\.com\/api\/stats\/ads _ reject-200
[MITM]
hostname = %APPEND% *.googlevideo.com, s.youtube.com
行的规则
- 空行忽略。
#或;开头的整行是注释。例外:#!开头的是元数据指令,会被解析。- 不支持行尾注释。
DOMAIN-SUFFIX,a.com,DIRECT # 走直连中的DIRECT # 走直连会被完整解析为出口名。该名称未定义时,规则会使用「默认」出口,且不会显示错误。注释必须独占一行并写在规则上一行。 [段名]单独占一行,开始一个新段。段名大小写不敏感。- 一条规则必须写在一行里,没有续行语法。
- 段的先后顺序不影响解析,同名段出现多次时内容累加。
元数据头
| 指令 | 作用 |
|---|---|
#!name=模块名 |
模块显示名 |
#!desc=一句话说明 |
模块描述 |
#!arguments=A:默认值,B:默认值 |
定义占位符 {{{A}}} 的默认值 |
其余 #! 指令(#!system、#!category、#!managed-config 等)不解析,写了也不报错。
占位符 {{{A}}} 在解析末尾展开,可用在模块名、描述、[MITM] 的 hostname、以及所有改写和脚本字段里。[Rule]、[Proxy Group]、[Host] 三段不展开占位符,写在那里就是字面的 {{{A}}}。客户端没有参数编辑界面,永远使用 #!arguments= 里写的默认值。所以除非要保留对其它客户端的兼容,直接把值写死更省事。
从文件导入的模块,列表里显示的是文件名(去掉扩展名),不是
#!name=。取个好文件名。
段落清单
| 段 | 作用 | 语法详见 |
|---|---|---|
[Rule] |
分流规则:什么流量走什么出口 | routing.md |
[Proxy Group] |
策略组:出口本身怎么组织 | routing.md |
[Host] |
域名的解析方式:静态映射,或指定用哪台 DNS 去查 | routing.md |
[General] |
只认 dns-server 一个键:改直连域名用哪台 DNS |
routing.md |
[Remote Rule] |
外部规则集订阅(Loon 写法) | routing.md |
[Remote Filter] |
按正则筛节点的具名分组(Loon 写法) | routing.md |
[MITM] |
对哪些域名做 HTTPS 解密 | mitm.md |
[URL Rewrite] |
改写或拦截请求 URL | mitm.md |
[Header Rewrite] |
增删改请求/响应头 | mitm.md |
[Body Rewrite] |
改写请求/响应正文 | mitm.md |
[Map Local] |
用本地内容直接回一个响应 | mitm.md |
[Script] |
用 JavaScript 处理请求/响应 | mitm.md |
不在这张表里的段一律被忽略,不报错、不影响其它段。常见的被忽略段:[Proxy]、[Panel]、[Replica]、[SSID Setting],以及各家客户端的其它私有段。[General] 除了 dns-server 之外的键也在此列。所以模块里不能用 [Proxy] 定义节点 —— 节点在客户端里管理。
模块中的 DNS 配置只控制目标域名的解析方式:[Host] 可为特定域名指定 DNS,也可使用保留值 server:fakeip 分配虚拟 IP,使 [Rule] 决定其出口;[General] dns-server 可替换直连域名使用的解析器。节点和入口域名的解析不受模块影响,始终使用客户端内置的解析路径。
格式与扩展名
导入时按扩展名判断源格式,认不出再看内容。
| 扩展名 | 格式 |
|---|---|
.sgmodule |
Surge |
.plugin |
Loon |
.stoverride |
Stash(YAML) |
.snippet |
Quantumult X |
.conf / .module |
Shadowrocket |
自己写就写 Surge 格式,存成 .sgmodule。 本手册全部语法以 Surge 为准。Loon / Stash / Quantumult X 的模块可以直接导入,但它们各自的私有写法只做到「不报错」,不保证逐字段等价。
导入与优先级
两种导入方式:
- 从文件导入:选本机的
.sgmodule文件。 - 从链接导入:填一个能公开访问的 URL,内容就是模块原文。之后可以按这个链接重新拉取更新。
同一个链接重复导入会原地覆盖,不会出现两份。
模块列表里越靠上优先级越高,可以拖动排序。多个模块的规则合在一起后,上面模块的规则排在下面模块之前。模块内部则是从上到下,先命中先生效。
模块的规则排在客户端自带的分流规则之前,所以模块可以刻意覆盖内置的国内直连之类的行为。
但客户端还有几条前置规则排在所有模块规则之前,模块盖不掉:
| 前置规则 | 后果 |
|---|---|
| 私网地址 → 直连 | 模块里针对 192.168.x.x、10.x.x.x 这类地址的规则永远不命中 |
| BitTorrent → 直连 | 模块管不了 BT 流量 |
| ICMP → 直连 | 模块不能修改 ping 流量的出口 |
| 「直连」/「全局」模式 | 用户切到这两个模式时,所有模块规则整体失效,流量一律按模式走 |
排查「规则写了没反应」时先想想是不是撞上了这几条。
独立规则列表
一份没有任何 [段名]、每行只有 类型,值 而没有出口列的纯规则文件(常见扩展名 .list)也能直接导入。导入时客户端会让你为整份列表选一个出口。
DOMAIN-SUFFIX,openai.com
DOMAIN-SUFFIX,chatgpt.com
DOMAIN-KEYWORD,openai
这种文件里的注释、payload: 头、以及 Clash YAML 的 - DOMAIN-SUFFIX,x 列表写法都能识别。
能用的类型比 [Rule] 少:只认 routing.md 规则类型全表里的那些,PROTOCOL、AND / OR、FINAL 在这种文件里一律被忽略。RULE-SET 与 [Remote Rule] 拉回来的外部规则集内容同理。