策略组类型与实战
策略组是配置文件里承上启下的一层:规则决定"哪类流量交给哪个组",策略组决定"这个组此刻用哪个节点"。分流做得是否顺手,八成取决于策略组结构设计得是否合理。mihomo 内核支持五种基础类型,行为差异如下。
五种类型的行为差异
| type | 行为 | 典型用途 |
|---|---|---|
| select | 手动选择,选中项持久保留直到下次手动切换 | 总入口组、地区选择组 |
| url-test | 周期性测延迟,自动切到延迟最低的节点 | 对速度敏感的日常流量 |
| fallback | 按列表顺序取第一个测活通过的节点,前面的恢复后自动切回 | 主备结构,保证服务不中断 |
| load-balance | 按散列或轮询把连接分散到多个节点 | 大量并发连接、下载类流量 |
| relay | 流量按列表顺序依次经过每个节点形成链 | 链式代理,特殊网络路径需求 |
select 是最常用也最容易被低估的类型:它不做任何自动判断,但正因如此行为完全可预期,适合做"人来拍板"的总入口。url-test 与 fallback 的区别经常被混淆——前者永远追求"当前最快",节点抖动时可能频繁横跳;后者追求"顺位最靠前的可用",只有当前节点失效才动,稳定性优先。对交互式应用(远程桌面、语音通话)来说,fallback 往往比 url-test 体验更好,因为切换意味着连接重建。
url-test 的三个关键参数
自动测速组的行为由三个参数控制。interval 是测速周期(秒),常见取 300;设得太短会产生大量探测请求,移动端还会拖累电池。tolerance 是容差(毫秒):新的最快节点必须比当前节点快出这个数值才触发切换,建议 50 起步,能显著减少无意义的横跳。lazy 设为 true 时,组在没有流量经过的时候跳过测速,对节点数量多的配置能省下可观的探测开销。
实战分组结构
推荐的结构是三层:顶层一个 select 总入口,中层按地区或用途分组,底层才是具体节点。规则永远指向顶层或中层组,不直接指向节点——这样换机场、换节点名都不需要动规则。示例:
proxy-groups:
- name: 节点选择
type: select
proxies: [自动测速, 故障转移, 香港节点, 日本节点, DIRECT]
- name: 自动测速
type: url-test
url: https://www.gstatic.com/generate_204
interval: 300
tolerance: 50
lazy: true
proxies: [HK-01, HK-02, JP-01]
- name: 故障转移
type: fallback
url: https://www.gstatic.com/generate_204
interval: 300
proxies: [HK-01, JP-01, SG-01]
- name: 香港节点
type: url-test
url: https://www.gstatic.com/generate_204
interval: 300
proxies: [HK-01, HK-02]
测活地址选返回 204 状态码的轻量端点(如上例的 generate_204),响应体为空、开销最小。不要用完整网页做测活,那测的是页面加载而不是链路延迟。
嵌套引用与命名一致性
策略组之间可以互相引用:一个 select 组的 proxies 列表里,既可以放具体节点,也可以放另一个 url-test 或 fallback 组的名字。这正是三层结构能成立的原因——顶层组把中层组当"节点"来选,中层组内部再自动测速。需要提醒的是,组名一旦被规则或其他组引用,就成了配置里的"接口":任何一处改名而引用方没同步,内核加载时会直接报"proxy group not found"并拒绝启动。批量调整分组时,养成"先全局搜索组名再改"的习惯,能省掉大量因为一个字符对不上而反复重载的时间。另外,组名尽量用能自解释的中文或简短英文,避免用纯符号或极易混淆的名字(比如同时存在"香港"和"香港节点"两个组),否则手动切换时很容易点错,排查时也难以一眼对应。
DIRECT、REJECT 与内置策略的位置
除了自定义组和具体节点,配置里还有三个内置策略必须理解:DIRECT 表示不经代理直接连接,REJECT 表示直接拒绝(常用于广告与追踪域名),PASS 表示跳过当前规则集交给后续规则继续判断。把 DIRECT 放进顶层 select 组的 proxies 列表是个实用技巧:遇到某个站点走代理反而更慢,或临时需要以真实 IP 访问某服务时,面板上一键切到 DIRECT 即可,不必去改规则。REJECT 一般不放进可选列表,而是直接作为广告类规则集的目标策略。理解这三者的语义,能避免"为什么这条流量没走代理"或"为什么这个域名连不上"这类看似诡异、实则策略指向问题的困惑。
规则集订阅化管理
把几百上千条规则直接写进主配置,是维护性灾难的开始:订阅一更新全部重来,想调一条规则要在长文件里翻找。规则集(rule-providers)把规则拆成独立的远程文件,主配置里只留引用,规则内容按周期自动更新,主配置保持十几行的清爽。
behavior 三种类型
每个规则集必须声明 behavior,它决定文件内容的解析方式,三者不可混用。domain 类型的文件只能包含域名(支持 +. 通配前缀),匹配效率最高;ipcidr 只能包含 IP 段(CIDR 表示法);classical 则允许写完整的规则语法(DOMAIN-SUFFIX、IP-CIDR、DST-PORT 等混排),灵活但匹配开销略大。原则是:能用 domain 或 ipcidr 就不用 classical,把 classical 留给确实需要混合条件的场景。
format 与更新周期
format 支持 yaml、text 与 mihomo 专有的二进制格式 mrs。mrs 是预编译产物,体积小、加载快,大体量的域名/IP 集合(比如整个地区的 IP 库)优先选它;自己手写维护的小规则集用 yaml 或 text 即可。interval 控制自动更新周期(秒),规则集内容变动频率低,86400(一天)是合理值,没必要更激进。
rule-providers:
streaming:
type: http
behavior: classical
format: yaml
url: https://example.com/rules/streaming.yaml
path: ./rule-sets/streaming.yaml
interval: 86400
cn-ip:
type: http
behavior: ipcidr
format: mrs
url: https://example.com/rules/cn-ip.mrs
path: ./rule-sets/cn-ip.mrs
interval: 86400
rules:
- RULE-SET,streaming,节点选择
- RULE-SET,cn-ip,DIRECT
- MATCH,节点选择
引用语法是 RULE-SET,规则集名,策略。注意规则仍然是自上而下逐条匹配、命中即停,RULE-SET 的排列顺序和普通规则一样重要:把命中率高的集合放前面,把 IP 类规则放在域名类规则之后(IP 规则会触发 DNS 解析,提前触发是浪费)。MATCH 兜底规则永远放最后一行。
behavior 声明与文件实际内容不匹配是规则集最常见的故障:比如把 classical 语法的文件声明成 domain,内核解析会直接报错或整个集合静默失效。引用第三方规则集时,先确认发布方标注的 behavior 类型再抄。
规则匹配顺序的性能含义
很多人把规则顺序只当成"逻辑对不对"的问题,其实它同样是"跑得快不快"的问题。内核对每条连接都要自上而下逐条比对,直到命中为止;规则越靠后,命中它的连接前面白白比对的次数就越多。当规则总量上万,顺序不当带来的比对开销在高并发下会变成可感知的延迟。优化的思路有两条:一是把高频命中的规则集尽量前置,让大多数连接在前几条就出结果;二是善用 no-resolve 参数——IP-CIDR 规则默认会先触发 DNS 解析拿到 IP 再比对,如果这条规则本意只针对已经是 IP 的直连流量,加上 no-resolve 就能跳过解析,既省时间又避免不必要的解析请求。
GEOIP 与 GEOSITE 的取舍
除了自建规则集,内核还内置支持 GEOIP 与 GEOSITE 两类地理数据库规则。GEOIP,CN,DIRECT 一行就能让所有归属中国大陆的 IP 直连,GEOSITE,category-ads-all,REJECT 一行拦掉一大类广告域名,写法极简。代价是数据库文件本身有体积、需要定期更新,而且分类的收录范围由数据库维护方决定,偶尔会出现"某个站点被归错类"的情况。实践中的平衡是:用 GEOSITE/GEOIP 兜住大面积的常规分流,再用少量精确的自定义规则集覆盖那些数据库判断不准、或有个性化需求的域名,并把自定义规则放在 GEO 规则之前,保证个性化判断优先生效。
DNS 配置优化
DNS 是分流质量的地基。域名解析被污染,IP 规则就会基于错误的 IP 做判断;解析服务器选择不当,直连流量会绕远、CDN 会分配到低速节点。内核自带一套完整的 DNS 模块,理解各字段的分工之后,大多数"规则明明写对了却不生效"的问题会自动消失。
nameserver 与 default-nameserver 的分工
nameserver 是主解析服务器列表,负责所有业务域名的解析,推荐用 DoH(DNS over HTTPS)地址以规避明文 53 端口的干扰。这里有个先有鸡还是先有蛋的问题:DoH 地址本身是个域名,它自己也需要被解析——这就是 default-nameserver 存在的意义,它只负责解析 nameserver 列表里出现的域名,必须填纯 IP 的传统 DNS。两者职责不同,不能互相替代。
nameserver-policy 按域名分派
nameserver-policy 允许按域名模式指定专用解析服务器:境内域名走境内 DNS 拿到就近 CDN,其余域名走可信的加密 DNS 避免污染。它支持 geosite 分类引用,一行就能覆盖一大类域名,是目前推荐的分流解析方案,比旧式的 fallback + fallback-filter 组合更直观、行为更可控。
dns:
enable: true
listen: 0.0.0.0:1053
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
default-nameserver:
- 223.5.5.5
- 119.29.29.29
nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
nameserver-policy:
"geosite:cn":
- https://doh.pub/dns-query
"geosite:geolocation-!cn":
- https://dns.cloudflare.com/dns-query
enhanced-mode 决定增强解析模式,取值 fake-ip 或 redir-host,与 TUN 的协同细节在下一章展开。listen 是内核 DNS 的监听地址,TUN 模式下配合 dns-hijack 把系统 DNS 查询劫持到这里,保证解析全部经过内核。
验证 DNS 配置是否生效,最直接的办法是看连接面板里域名解析出的 IP 归属:境内站点解析出境内 CDN、其余站点没有出现明显异常 IP,基本就配对了。解析行为异常时优先检查 default-nameserver 是否可达。
TUN 与 Fake-IP
系统代理模式的天生短板是"只管遵守代理设置的程序":命令行工具、游戏、部分客户端软件根本不读系统代理。TUN 模式在系统里创建一块虚拟网卡,把全部流量在网络层接进内核,从根上解决接管不全的问题。UWP 应用的回环限制、终端里的 git 与包管理器,在 TUN 模式下都不再需要单独处理。
启用 TUN 与 stack 选择
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
auto-route 自动接管系统路由,auto-detect-interface 自动识别物理出口网卡,两项通常都保持开启。dns-hijack 把发往任意 53 端口的查询劫持给内核 DNS,是 TUN 与 Fake-IP 协同的关键一环。stack 决定虚拟网卡的协议栈实现:system 直接用操作系统网络栈,吞吐好;gvisor 是用户态实现,兼容性好;mixed 取两者折中,TCP 走 system、UDP 走 gvisor,桌面端可以从 mixed 开始试。各平台启用前提不同:
| 平台 | 启用前提 | 备注 |
|---|---|---|
| Windows | 管理员权限运行,或安装客户端提供的系统服务 | Clash Verge Rev 提供服务模式,免每次提权 |
| macOS | 首次启用需输入管理员密码授权 | 授权一次后续静默生效 |
| Android | 走系统 VpnService 接口,授权 VPN 连接即可 | 无需 root,后台策略见下方提示 |
| Linux | root 权限或授予二进制 CAP_NET_ADMIN 能力 | 服务器场景建议 systemd 常驻 |
Fake-IP 原理与 fake-ip-filter
Fake-IP 是配合 TUN 的解析策略:程序发起域名查询时,内核不等真实解析完成,立即从保留网段(默认 198.18.0.1/16)返回一个"假 IP"并记住映射关系。连接到达时凭假 IP 反查出域名,直接按域名规则匹配——省掉一次串行的 DNS 等待,建连更快,规则命中也更准。代价是有些程序拿到假 IP 会出问题:局域网发现、时间同步、网络连通性检测这类"拿 IP 当真"的场景需要豁免,这就是 fake-ip-filter 的用途:
dns:
fake-ip-filter:
- "*.lan"
- "+.local"
- "time.windows.com"
- "+.msftconnecttest.com"
- "+.stun.*.*"
命中过滤列表的域名走真实解析、返回真 IP。列表宜精不宜滥:加得越多,Fake-IP 的收益被稀释得越厉害。另外注意 Fake-IP 与 redir-host 切换后,系统和浏览器里可能残留旧的解析缓存,表现为一段时间内部分站点行为怪异,重启客户端或刷新系统 DNS 缓存即可恢复。
移动端长期开 TUN 对功耗有可感知的影响,后台保活与厂商省电策略的取舍见博客《Clash 安卓耗电异常分析》;桌面端启动即崩、TUN 启用失败一类问题在常见问题的故障排查分类里有逐项处理办法。
域名嗅探
规则里写得最顺手的是域名规则,但有两类流量到达内核时只有 IP、没有域名:一是程序自己做了 DNS 解析后直连 IP(绕开了内核的解析环节),二是 Fake-IP 映射之外的场景。没有域名,DOMAIN-SUFFIX 一类规则全部落空,流量只能掉进 IP 规则或兜底规则。域名嗅探(sniffer)的作用就是从流量本身把域名"读"回来:TLS 握手的 SNI 字段、HTTP 请求的 Host 头、QUIC 的握手包里都带着目标域名,内核在建连初期解析这些协议特征,把还原出的域名重新交给规则引擎匹配。
配置结构
sniffer:
enable: true
sniff:
HTTP:
ports: [80, 8080-8880]
override-destination: true
TLS:
ports: [443, 8443]
QUIC:
ports: [443]
force-domain:
- "+.example-cdn.net"
skip-domain:
- "+.push.apple.com"
sniff 下按协议声明嗅探的端口范围,只嗅探声明过的端口,范围收得越窄开销越小。override-destination 决定嗅探出域名后是否用它覆盖连接的目标地址——开启后连接会以域名形式继续处理,规则匹配与 DNS 分派都按域名走。force-domain 强制对命中的域名执行嗅探覆盖,常用于某些拿到 Fake-IP 仍自行连接 IP 的应用;skip-domain 则是白名单,命中的域名跳过覆盖,推送服务这类对连接目标敏感的长连接建议列入,避免嗅探干扰。
嗅探不是解密:它只读取协议握手阶段明文携带的元数据(SNI、Host),不触碰加密载荷。对绝大多数配置,开启 HTTP 与 TLS 两类嗅探已经能覆盖主要场景,QUIC 按需追加。
QUIC 嗅探与 HTTP/3 的现实取舍
越来越多的站点默认启用 HTTP/3,而 HTTP/3 跑在 QUIC(UDP 443)之上。这带来一个经常被忽略的连锁反应:如果只嗅探 TLS 而不处理 QUIC,支持 HTTP/3 的浏览器会优先走 QUIC,内核拿到的是一条只有 IP、没有 SNI 域名的 UDP 流量,域名规则随之落空。应对方式有两种,取舍取决于你更看重命中准确性还是链路性能。一种是打开 QUIC 嗅探,让内核也能从 QUIC 握手里还原域名;另一种更简单粗暴——直接用规则拦掉 UDP 443,逼浏览器回退到 TCP 上的 HTTP/2,虽然损失了 HTTP/3 的部分性能,但换来了完全可预期的域名分流。家用环境如果发现"某些走了代理的站点偶尔抽风、时好时坏",十有八九就是 QUIC 流量没被正确接管导致的,这条经验值得记住。
本地覆写与多订阅合并
直接在订阅下发的配置文件上改动,是新手最常见的维护陷阱:订阅一更新,机场服务器的模板会整体覆盖本地文件,所有手改瞬间归零。博客《Clash 订阅更新失败怎么排查》里相当一部分案例的根源就在这里。正确姿势是把"机场给的"与"自己写的"分离:订阅只当节点来源,个性化内容放进覆写层或独立的 provider。
客户端覆写:Merge 与 Script
Clash Verge Rev 提供两级覆写,都作用于订阅更新之后、内核加载之前,因此永远不会被订阅冲掉。Merge 覆写是声明式的:写一段 YAML 片段,按字段合并进最终配置,适合追加 DNS 配置、rule-providers、置顶几条自定义规则这类结构化改动。Script 覆写是一段 JavaScript 函数,接收完整配置对象、返回修改后的对象,适合需要逻辑的场景——按名称正则过滤节点、批量给策略组塞节点、根据节点数量动态生成分组。两者可叠加使用,建议优先 Merge,确实需要编程能力再上 Script。Clash Plus 同样提供订阅与本地配置分离的管理方式,各客户端在这方面的差异可参考客户端对比。
proxy-providers 多订阅合并
手里有多家订阅时,不必来回切换配置。proxy-providers 把每个订阅声明为一个节点来源,各自独立更新,再通过策略组的 use 字段把多个来源的节点汇入同一个组:
proxy-providers:
airport-a:
type: http
url: https://a.example.com/sub?token=xxxx
path: ./providers/airport-a.yaml
interval: 43200
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 600
airport-b:
type: http
url: https://b.example.com/sub?token=xxxx
path: ./providers/airport-b.yaml
interval: 43200
proxy-groups:
- name: 节点选择
type: select
use: [airport-a, airport-b]
- name: 自动测速
type: url-test
url: https://www.gstatic.com/generate_204
interval: 300
use: [airport-a, airport-b]
use 与 proxies 可以在同一个组里并存,前者引入 provider 的全部节点,后者追加手写节点或其他组。health-check 让 provider 自带测活,url-test 组引用时能直接复用测活结果。interval 是订阅拉取周期(秒),43200(半天)对多数机场足够;某个 provider 拉取失败不影响其他来源,这也是多订阅结构天然的容灾优势。
用 provider 的 filter 字段(正则)可以只放行特定节点进组,例如按地区名筛选,配合 use 能做出"多机场同地区聚合测速"的结构,规则层完全无感知。
外部控制面板
内核运行时暴露一套 RESTful 控制接口,节点切换、延迟测试、连接查看、配置重载都通过它完成——桌面客户端的界面本质上就是这套接口的封装。直接使用外部控制,价值在两类场景:一是软路由、服务器上跑裸内核,没有 GUI;二是想用浏览器面板或脚本做自动化管理。
开启接口与鉴权
external-controller: 127.0.0.1:9097
secret: "your-strong-secret"
external-ui: ./ui
external-controller 声明监听地址与端口。只在本机使用就绑 127.0.0.1;需要局域网内其他设备访问(比如用手机管理软路由上的内核)才绑 0.0.0.0,并且此时 secret 必须设置为足够强的随机串——所有请求都要在 Authorization 头里携带它。external-ui 指向一个静态网页面板目录,内核会直接托管,浏览器访问控制地址即可打开;主流的开源 Web 面板(metacubexd、yacd 系)解压进该目录就能用。
用 API 做自动化
接口是标准 HTTP + JSON,curl 就能驱动。查看全部策略组与节点状态:
切换某个 select 组的选中节点,对组名发 PUT 请求:
常用端点还包括 /connections(实时连接列表,排查"这条流量到底走了哪个策略"的第一现场)、/logs(日志流)、/configs(运行时改端口与模式)。把这些端点接进脚本,就能实现定时测速、异常自动切换一类的无人值守逻辑。
控制接口拥有对内核的完全控制权。绑定非回环地址而不设 secret,等于把代理控制权敞开给整个局域网;暴露到公网则任何情况下都不可接受。示例里的 secret 是占位写法,实际使用请替换为自己生成的随机值。
面板与客户端并存时的状态一致性
桌面客户端在运行时会连着同一套控制接口,因此当你同时开着客户端界面又用浏览器面板或脚本操作时,双方看到的是同一份内核状态:在浏览器里切了节点,客户端界面刷新后也会显示新的选择。这一点在做自动化时尤其要留意——脚本切换节点属于对内核的"真实操作",会被持久记录,不像单纯测速那样只读。因此建议把自动化脚本的行为限定在明确的场景里(比如检测到当前节点连续测活失败才切换),并在日志里留下切换记录,避免出现"节点莫名其妙自己变了、又找不到是谁改的"的困惑。软路由等无 GUI 环境更要如此,因为那里没有界面帮你直观回看状态。
端口与冲突排查
控制端口(示例里的 9097)和内核的代理监听端口(mixed-port、DNS 的 listen 端口等)是各自独立的,它们之间不能撞车,也不能和系统里其他常驻服务抢同一个端口。启动失败时,"address already in use"是最典型的报错,含义就是某个端口被占了。排查顺序很简单:先确认是不是上一个内核实例没退干净(残留进程占着端口),再用系统的端口占用查看命令定位到底是谁在用,最后要么关掉占用方,要么把内核的端口改到一个没人用的值。这类端口冲突在同时装了多个代理客户端、或客户端异常退出后重启时特别常见,理解了各端口的独立性,就不会再把它误当成"配置写错了"来反复折腾配置文件。
查阅路径与延伸阅读
七章内容之间有清晰的依赖关系:策略组与规则集是骨架,DNS 是地基,TUN、Fake-IP 与嗅探解决"接管得全不全、匹配得准不准",覆写与合并解决长期维护,外部控制负责运行时观测与自动化。建议按实际痛点单章切入,不必从头通读。配置里遇到陌生名词,术语表按"内核与客户端 / 代理协议 / 规则与策略组 / DNS 与流量处理 / 配置文件字段"五个分类收录了定义;改完配置出现报错或行为异常,先查常见问题的故障排查分类;还没决定用哪个客户端承载这些配置,客户端对比给了按平台与使用习惯的选型结论,首次安装的完整检查单见博客《Clash 客户端首次安装设置清单》。本页会随内核配置字段的演进持续修订,建议收藏备查。