阅读前提与配置文件结构
继续之前,请确认两件事:客户端已经能正常联网(教程页的主线走完了),并且你知道自己当前用的配置来自哪份订阅。进阶配置的每一处改动都建立在"有一份能跑的基线"之上,基线不稳,后面的调整无从验证。
mihomo 的配置文件是一份 YAML,顶层键可以按职责分成五块,本手册的章节安排与之一一对应:
- 入站:
port、socks-port、mixed-port、tun——流量从哪里进来; - 出站:
proxies、proxy-providers、proxy-groups——流量可以从哪里出去; - 路由:
rules、rule-providers——哪类流量走哪个出口; - 解析:
dns、sniffer——域名如何变成地址,地址如何还原成域名; - 控制:
external-controller、external-ui、secret——运行状态如何对外暴露。
最小可运行骨架
剥掉所有可选项,一份能启动的配置长这样。后续各章的示例片段,都默认挂在这副骨架的对应位置上:
mixed-port: 7890
mode: rule
log-level: info
proxies: [] # 通常由订阅提供
proxy-groups:
- name: 节点选择
type: select
proxies:
- DIRECT
rules:
- GEOIP,CN,DIRECT
- MATCH,节点选择
YAML 对缩进极其敏感:统一用两个空格,禁止 Tab;值里含冒号、井号或以特殊字符开头时要加引号。启动闪退的第一嫌疑人永远是缩进,排查思路可参考博客《Clash 启动闪退怎么办》。
平台差异:iOS 与桌面端
iOS 上的 Clash Plus 基于 Network Extension 运行,配置管理方式是"订阅为底 + 覆写为增量"——你一般不直接改订阅原文,而是在客户端里叠加自己的修改(详见第六章)。桌面端的 Clash Verge Rev、FlClash 则允许直接编辑配置全文。无论哪个客户端,本手册讨论的都是内核层语义,写法通用。
最后是一个值得养成的习惯:每次改动配置后,固定走"重载配置 → 观察日志 → 验证行为"三步。日志各级别的阅读顺序与高频报错含义,博客《Clash 运行日志怎么看》有逐条解释,本手册后面几章会反复用到这套验证方法。
配置来源、备份与回滚
进阶配置的另一半功课是版本管理。手册里的每一章都会让你往配置里加东西,而加错东西的代价往往不是立刻报错,而是三天后某个站点突然打不开。因此在动手之前,先给当前能跑的配置留一份副本:桌面端可以直接把配置目录复制成带日期的文件夹,iOS 上则把覆写内容单独抄到备忘录或私有仓库里。每次只改一个主题——这一次只动 DNS,下一次只动策略组——改完立刻验证,确认无误再叠加下一项。这样即使出问题,你也能准确说出是哪一处改动带来的,回滚一步即可恢复,而不必推翻整份配置重来。
还要区分清楚三类「配置来源」:订阅服务商下发的原文、客户端自动生成的运行时配置、以及你自己维护的覆写片段。真正需要你长期保管的只有第三类,前两类随时可以重新拉取或重新生成。把这条界线记牢,后面第六章讲覆写时你会更容易理解「为什么不要直接改订阅原文」。
策略组类型与实战编排
策略组(proxy-groups)是规则与节点之间的中间层。规则命中后,目标可以是单个节点、内置出口(DIRECT / REJECT),也可以是一个策略组;而策略组的成员,又可以是节点或另一个策略组。理解"组可以套组"这一点,是编排一切复杂分流的前提。
mihomo 提供五种组类型,选择逻辑各不相同:
| 类型 | 选择逻辑 | 典型场景 |
|---|---|---|
select | 手动选择,保持不变直到用户再次切换 | 总入口组、地区手选组 |
url-test | 周期性测速,自动锁定延迟最低的成员 | 同地区节点自动择优 |
fallback | 按列表顺序取第一个可用成员,失效才后移 | 主备切换、保底出口 |
load-balance | 按散列或轮询把连接分散到多个成员 | 多节点分摊大流量 |
relay | 成员按顺序串联成链式转发 | 特殊链路需求,日常少用 |
自动测速组的四个关键参数
url-test 与 fallback 依赖健康检查,行为由四个参数决定。url 是测速目标,惯例用返回 204 的轻量地址;interval 是检查周期(秒),300 是稳妥值,压得太低会白白消耗节点流量;tolerance 是切换容差(毫秒)——新旧节点延迟差距小于该值时不切换,用来防止两个延迟接近的节点来回抖动;lazy: true 表示组未被使用时暂停测速,移动设备上建议开启以省电省流量。
实战:三层结构
大多数场景下,一个清晰的配置只需要三层:手动总入口在最上,自动测速与故障转移在中间,订阅节点在最下。规则一律指向总入口,日常切换只碰这一个组:
proxy-groups:
- name: 节点选择
type: select
proxies:
- 自动测速
- 故障转移
- DIRECT
- name: 自动测速
type: url-test
url: https://www.gstatic.com/generate_204
interval: 300
tolerance: 50
lazy: true
use:
- main-sub
- name: 故障转移
type: fallback
url: https://www.gstatic.com/generate_204
interval: 300
use:
- main-sub
示例里的 use 字段引用的是代理集合(proxy-providers,第六章展开),让组的成员随订阅更新自动同步,不必手写节点名。两个注意点:组名在 rules 里引用时必须一字不差,多一个空格都会导致启动失败;组与组互相引用时不能形成环,内核会在启动时直接报错拒绝加载。
需要按业务再细分(比如流媒体单独走一组)时,照同样的模式加一个 select 组、把「自动测速」「节点选择」放进成员即可——先有骨架再做加法,比一上来堆十几个组好维护得多。
负载均衡与链式转发的取舍
表格里的 load-balance 与 relay 日常用得少,但值得知道它们的适用边界。load-balance 有两种散列策略:按连接散列会把每条新连接分给不同成员,吞吐更均匀,但同一站点的多个请求可能来自不同出口 IP,登录态敏感的网站会因此频繁要求重新验证;按域名散列则保证同一域名始终落在同一成员上,兼容性更好,是日常首选。relay 把多个节点串成一条链路,每多一跳就多一段延迟与一处故障点,只有在「必须先落地到某个中转再出网」的特殊需求下才考虑。
组的命名同样值得花一分钟规划。组名会同时出现在规则里、客户端界面上、以及外部控制 API 的路径中,后期改名意味着所有引用处一起改,还可能撞上 URL 编码问题。建议一开始就用短、稳、语义明确的名字,避免使用空格开头结尾与容易混淆的全角符号;把节点的地区、倍率信息留在节点名里,不要塞进组名。若某个组的成员需要按关键词筛选,用 filter 字段配合正则从集合里挑选,比手写成员列表更能扛住订阅变动。
规则集订阅化管理
把几千行规则直接写进 rules 有两个代价:配置本体臃肿难读,规则一变就得改主配置。rule-providers 的思路是把规则外置成独立文件,内核按周期自动拉取更新,主配置里只留一行引用。
rule-providers:
streaming:
type: http
behavior: domain
format: yaml
url: https://example.com/rules/streaming.yaml
path: ./rule-sets/streaming.yaml
interval: 86400
rules:
- RULE-SET,streaming,节点选择
- GEOIP,CN,DIRECT
- MATCH,节点选择
字段逐个说:type 取 http(远程拉取)或 file(本地文件);url 与 path 分别是远程地址和本地缓存路径;interval 是自动更新周期(秒),规则集内容变化不频繁,86400(一天)足够。format 支持 yaml、text 和 mrs——mrs 是 mihomo 的二进制规则格式,体积小、加载快,规则源提供时优先选它。
behavior:最容易踩的一个字段
behavior 声明这份规则集的内容形态,必须与文件实际内容匹配:
| behavior | 内容形态 | 示例行 |
|---|---|---|
domain | 纯域名列表(支持 +. 通配) | +.example.com |
ipcidr | 纯 IP 段列表 | 203.0.113.0/24 |
classical | 完整规则行(自带匹配类型) | DOMAIN-SUFFIX,example.com |
behavior 与内容不匹配时,规则集往往静默失效——不报错,只是永远不命中。分流"看起来配了但不生效"时,先核对这里。另外,ipcidr 类规则集在 RULE-SET 行末尾可加 no-resolve,避免为纯 IP 匹配触发一次额外的域名解析。
与 GeoSite / GeoIP 的关系
GEOSITE 与 GEOIP 规则背后是另一种"打包好的规则集"——两个二进制数据库,按国家或站点分类收录域名与 IP 段。它们与 rule-providers 并不冲突:geo 数据库覆盖通用大类,rule-providers 补充个性化条目。数据库过旧会直接导致分流失准,更新方式见博客《GeoIP 与 GeoSite 数据库更新指南》;至于 rules 里各类型的语法与自上而下的优先级原则,博客《Clash 自定义规则写法详解》有完整展开,此处不重复。
规则集的更新失败与本地兜底
rule-providers 依赖网络拉取,而拉取本身也可能失败:规则源域名解析被污染、源站临时不可用、或者拉取时机恰好在代理还没建立的启动瞬间。内核的处理策略是「拉不到就用本地缓存」,因此 path 指向的缓存文件其实是一道兜底防线,首次成功拉取之后就不要随手删掉它。日志里出现规则集拉取超时的告警,而分流依然正常,说明正在吃缓存;此时不必惊慌,但要留意规则内容是否已经过期。
如果某个规则源长期不稳定,更稳妥的做法是把它固定成 type: file 的本地规则集,自己定期手动更新一次。这种方式牺牲了自动化,换来的是启动阶段的确定性——对路由器等无人值守场景尤其重要。
规则条数、顺序与性能
很多人担心规则太多会拖慢速度,实际瓶颈几乎从不在这里:域名类规则在内核里以前缀树等结构组织,几万条的匹配开销依然可以忽略。真正值得关注的是顺序与解析代价。顺序方面,越具体的规则越要往前放,兜底的 MATCH 永远在最后一行;把 GEOIP,CN,DIRECT 误放在自定义域名规则前面,是「规则明明写了却不生效」的经典原因。解析代价方面,所有 IP 类规则(GEOIP、IP-CIDR)在遇到域名目标时都需要先解析一次,若不加 no-resolve,一条位置靠前的 IP 规则会给每个新域名连接都引入一次解析等待。因此惯例是:域名规则集中放在前半段,IP 规则放在后半段,并对无需解析的场景显式声明 no-resolve。
最后给一个可长期维护的规则布局模板:第一段是本地与内网直连(DOMAIN-SUFFIX,lan、私有地址段),第二段是自己的强制规则(prepend 上来的白名单黑名单),第三段是各类订阅化规则集,第四段是 geo 大类,最后一行 MATCH 指向总入口组。按这个分段组织,任何一条新规则都能立刻找到自己该待的位置。
DNS 配置优化
先回答"为什么内核要自己管 DNS"。规则分流大量依赖解析结果:GEOIP,CN,DIRECT 要先把域名解析成 IP 才能判断归属。如果解析交给系统、而系统拿到的是被污染的地址,GEOIP 会把本该走代理的流量误判成直连,表现为"规则没错但就是打不开"。让内核用可信的加密 DNS 自己解析,是分流准确性的地基。
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
nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
proxy-server-nameserver:
- https://doh.pub/dns-query
nameserver-policy:
"geosite:cn":
- https://doh.pub/dns-query
三组 nameserver 各管一段
这套字段容易混,按"谁被解析"来记:default-nameserver 只能填纯 IP,专门用来解析 nameserver 里 DoH 地址自己的域名——鸡生蛋问题的引导器;nameserver 是主力解析器,承担所有常规查询,推荐 DoH(https:// 前缀)或 DoT(tls:// 前缀)写法,明文 53 端口在部分网络环境下会被劫持;proxy-server-nameserver 单独负责解析代理节点自身的服务器域名,把它固定到国内直连 DoH,可以避免"节点域名解析要走代理、代理又依赖节点"的死锁。
nameserver-policy:按域名分派解析器
nameserver-policy 允许按域名模式指定解析器,最常用的一条就是示例里的 geosite:cn:国内站点固定用国内 DoH 解析,拿到就近的 CDN 地址;其余域名交给 nameserver 主力。键也可以写普通域名模式(如 "+.internal.example.com" 指向内网 DNS),适合办公网场景。
验证 DNS 配置是否生效,最直接的办法是看日志里的解析记录:哪个域名、哪台解析器、返回什么。出现成批的 dns resolve failed 时,优先检查 default-nameserver 是否可达,排查路径见博客《Clash 运行日志怎么看》。
示例里的 enhanced-mode: fake-ip 是 DNS 与 TUN 交界处最重要的开关,值得单开一章——往下看。
DoH、DoT 与明文 53 的现实取舍
三种传输方式的差别不只在「是否加密」。明文 UDP 53 延迟最低,但请求内容对链路上的任何设备都是可见的,也最容易被中间设备改写应答;DoT 走 TCP 853 端口,加密完整,但端口特征明显,在一些网络里会被整体阻断;DoH 把查询包裹进 HTTPS,和普通网页流量混在一起,最难被区分对待,代价是每次冷启动要多一次 TLS 握手。实践上的组合是:nameserver 用两条不同服务商的 DoH 互为备份,default-nameserver 只填一两个可靠的纯 IP 明文解析器用于引导。不要在 nameserver 里堆七八条,内核会并发查询并取最快应答,条目过多只是徒增流量与不确定性。
解析结果异常的排查顺序
DNS 相关的故障表现高度雷同——「能连上但打不开」「部分站点白屏」——排查时按固定顺序走最省时间。第一步确认 dns.enable 为 true 且监听端口没被其他程序占用;第二步在日志里搜索目标域名,看是哪台解析器应答、返回了什么地址;第三步判断该地址是否合理,国内站点却拿到境外地址、或者反过来,通常意味着 nameserver-policy 分派错了;第四步才去怀疑规则本身。respect-rules 之类的高级开关会让解析请求也遵循分流规则,能解决少数场景的精准解析问题,但它引入了「规则依赖解析、解析又依赖规则」的循环,不熟悉时不要轻易打开。
缓存、TTL 与内网解析
内核会缓存解析结果,缓存命中时应答几乎是零延迟,这也是切换配置后旧地址仍然生效一段时间的原因。遇到需要立刻见效的场景,重载配置或重启客户端网络即可清空缓存。若你所在的环境有内网域名(公司 OA、NAS 面板、局域网打印机),务必在 nameserver-policy 里把对应后缀指向内网 DNS,并在下一章的 fake-ip-filter 里放行它们——这两处配合起来,才能保证内网服务在开启代理时依然可达。
TUN 模式与 Fake-IP
系统代理只能覆盖"愿意读代理设置"的应用,命令行工具、部分游戏与后台服务会直接绕过。TUN 模式的做法更彻底:创建一块虚拟网卡,配合路由表把设备的全部流量引进内核处理——应用无从绕过,因为它们发出的每个包都要经过这块网卡。
平台差异先说清楚:iOS 上的 Clash Plus 基于 Network Extension 的隧道机制运行,行为天然等价于 TUN,不需要也没有额外的开关;下面的 tun 配置块主要面向桌面端与路由器场景(Windows 需要管理员权限装载虚拟网卡驱动,macOS 需要在系统设置里批准网络扩展)。路由器上直跑内核的整体思路,见博客《路由器直跑 mihomo 内核》。
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
stack 决定协议栈实现:system 用系统栈,性能好但兼容性看平台;gvisor 用用户态栈,兼容性稳;mixed 取两者所长,一般直接选它。auto-route 自动写路由表,auto-detect-interface 自动识别物理出口网卡防止回环,这两项保持开启。dns-hijack: any:53 把所有发往 53 端口的明文 DNS 查询劫持给内核解析——这是 TUN 模式下保证"解析必经内核"的关键一环。
Fake-IP:少一次等待的解析模式
回到上一章的 enhanced-mode。传统 redir-host 模式下,内核收到 DNS 查询要真实解析一次才能应答;fake-ip 模式则立即从保留网段(默认 198.18.0.1/16)里分配一个假地址返回,同时记住"这个假 IP 对应这个域名"。应用拿假 IP 发起连接时,内核按映射还原出域名去匹配规则——走代理的流量把域名直接交给远端解析,本地那次真实解析被完全省掉,建连更快,域名匹配也更准。
| 对比项 | fake-ip | redir-host |
|---|---|---|
| 应答速度 | 即时返回假地址 | 等待真实解析完成 |
| 规则匹配 | 始终拿得到域名,域名规则全量生效 | 部分场景退化为按 IP 匹配 |
| 兼容性 | 少数依赖真实 IP 的场景需要过滤 | 无假地址副作用 |
兼容性问题靠 fake-ip-filter 解决:凡是必须拿到真实 IP 的域名——局域网设备发现、NTP 校时、部分游戏对战平台——列进去让它们走真实解析:
dns:
fake-ip-filter:
- "*.lan"
- "+.local"
- time.apple.com
- "+.stun.*.*"
在 fake-ip 与 redir-host 之间切换后,系统与浏览器里可能残留旧模式的解析缓存,表现为一段时间内部分站点打不开。切换后重启客户端网络连接,必要时重启设备,再做验证。
TUN 与系统代理的共存问题
桌面端最常见的翻车场景是 TUN 与系统代理同时开着。两者本身可以共存,内核也会处理重复入站,但一旦系统代理指向的端口和 TUN 劫持的流量互相绕圈,就会出现连接莫名超时、日志里同一域名反复建连的现象。稳妥的做法是二选一:需要覆盖全部应用就只开 TUN,只想代理浏览器就只开系统代理。若确实要同时开启,请确认 auto-detect-interface 为 true,让内核始终把节点流量送往真实物理网卡,而不是又送回虚拟网卡形成回环。
另外两个平台细节值得提前知道。虚拟网卡的装载需要系统级权限,Windows 上首次启用会弹出驱动安装提示,macOS 上需要在系统设置里手动允许网络扩展;这两步没通过时,客户端界面会显示已开启 TUN,但实际没有任何流量进来。还有一类问题来自其他网络软件:部分企业 VPN、安全客户端也会写路由表,与 auto-route 争抢默认路由,表现为开启 TUN 后整机断网。遇到这种情况,先退出另一方再测试,确认冲突源之后再考虑用更细的路由排除规则共存。
Fake-IP 网段与常见误解
关于 fake-ip 有几个反复被问到的点。第一,假地址只在本机有意义,永远不会出现在网络上,因此不必担心它和公网地址冲突;但 fake-ip-range 必须避开你实际使用的局域网段,默认的 198.18.0.1/16 属于保留测试网段,通常是安全的。第二,某些应用会把解析到的 IP 缓存很久甚至写进配置文件,关闭代理后这些假地址就成了死地址,表现为「关了代理反而更打不开」——退出并重启该应用即可。第三,IP 类规则在 fake-ip 下的行为取决于内核能否还原域名:能还原时按域名匹配,还原不了才落到 IP 规则,这也是下一章嗅探要补的那一环。
域名嗅探:找回丢失的域名
前两章解决了"域名如何解析",但还有一类流量根本不经过内核的 DNS:应用自带加密 DNS(比如浏览器内置 DoH)、或干脆把服务器 IP 写死在代码里。这类连接到达内核时只有一个目的 IP,配置里精心维护的域名规则(DOMAIN-SUFFIX、GEOSITE)对它全部失效,只能靠 IP 规则兜底,分流精度大打折扣。
域名嗅探(sniffer)的做法是从流量本身把域名找回来:TLS 握手的 ClientHello 里带着 SNI,HTTP 请求头里有 Host,QUIC 初始包里同样能还原出目标域名。内核在建连初期读取这些字段,把还原出的域名回填给路由引擎,让域名规则重新生效。
sniffer:
enable: true
sniff:
HTTP:
ports: [80, 8080-8880]
override-destination: true
TLS:
ports: [443, 8443]
QUIC:
ports: [443, 8443]
force-domain:
- "+.v2ex.com"
skip-domain:
- "Mijia Cloud"
三个协议块分别声明在哪些端口上尝试嗅探对应协议;override-destination 表示用嗅探到的域名覆盖连接的目标地址,通常保持开启。force-domain 列出的域名即便解析结果正常也强制嗅探覆盖,用于对付部分 CDN 域名与 IP 对不上的情况;skip-domain 则相反,把嗅探后反而出问题的目标排除掉——某些智能家居私有协议会被误识别,加进这里即可。
代价方面,嗅探只读取每条连接最初的握手数据,开销可以忽略。它与 fake-ip 是互补关系而非替代:fake-ip 保证经过内核 DNS 的流量带着域名,嗅探负责补上绕过内核 DNS 的那部分。两者同时开启,域名规则的覆盖面才算完整。
什么时候该怀疑嗅探
嗅探带来的问题通常有很鲜明的特征:某个应用在开启嗅探后完全连不上,而关掉嗅探立刻恢复。原因多半是它的私有协议在指定端口上跑,而握手数据被误当成 TLS 解析,拿到一个乱码「域名」后连接被送去了错误的出口。智能家居设备、局域网内的私有服务、部分游戏的对战通道都属于高发区。处理方式有三档:先用 skip-domain 排除已知目标,再考虑把该端口从 sniff 的端口列表里删掉,最后才是整体关闭嗅探。另外注意 override-destination 的语义——关掉它时嗅探到的域名只用于规则匹配,不覆盖实际连接目标,这是一个更保守也更安全的折中档位。
验证嗅探是否在工作
最直观的验证方式是看活动连接列表:嗅探生效时,原本只显示 IP 的连接会带上还原出的域名,通常还会标注嗅探来源。若列表里大量连接依然只有 IP,先确认目标端口是否在 sniff 声明的范围内(很多服务跑在非标准端口上),再确认这些连接是不是 QUIC——浏览器的 QUIC 流量若没在 QUIC 块里声明端口,就不会被嗅探。把 HTTP、TLS、QUIC 三块都配好,才能覆盖当下主流的流量形态。
本地覆写与多订阅合并
一个所有人都会撞上的问题:订阅文件是服务端生成的,你直接在里面加的规则、改的 DNS,下一次订阅更新就被整体覆盖冲掉。正确做法是让订阅保持原样,把自己的修改声明成覆写——每次订阅更新后,客户端自动把覆写重新叠加上去。
两种覆写形态
主流客户端提供两类覆写。声明式(merge):写一份 YAML 片段,声明哪些顶层键要替换、哪些列表要在头尾追加,直观且不易出错;脚本式:写一个函数,接收完整配置对象、返回修改后的配置,灵活但需要自担调试成本。Clash Plus 与 Clash Verge Rev 都内置了覆写入口,日常需求用声明式就够了。以常见的 merge 语义为例:
# 覆写片段:整键替换 dns,规则头部追加两条
dns:
enable: true
enhanced-mode: fake-ip
prepend-rules:
- DOMAIN-SUFFIX,internal.example.com,DIRECT
- PROCESS-NAME,Terminal,DIRECT
理解 merge 只需要记住一条:普通顶层键是整体替换,带 prepend / append 前缀的键是列表追加。规则自上而下匹配,自定义规则想优先生效就用头部追加;想只做兜底就用尾部追加。
多订阅合并:proxy-providers
手里有多份订阅时,不必在客户端里来回切换配置。proxy-providers 把每份订阅声明成一个节点集合,策略组用 use 引用集合,所有订阅的节点就汇入同一份配置:
proxy-providers:
main-sub:
type: http
url: https://example.com/sub?token=xxxx
path: ./providers/main.yaml
interval: 43200
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 600
backup-sub:
type: http
url: https://backup.example.com/sub?token=xxxx
path: ./providers/backup.yaml
interval: 43200
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 600
字段与第二章的 rule-providers 同构:interval 控制订阅自动更新周期,43200(半天)对多数订阅足够;health-check 让集合内节点带上可用性状态,供 url-test / fallback 组直接消费。第一章示例里 use: [main-sub] 引用的正是这里——组的成员随订阅更新自动增减,配置本体一行不用改。订阅更新失败时先看日志里的拉取报错,再核对 URL 是否过期。
订阅 URL 里的 token 等同于账号凭据:不要截图、不要贴进公开的问题反馈里。示例中的 token=xxxx 是占位假值。
多订阅场景下的组编排
把两份订阅都接进来之后,策略组该怎么组织?不推荐把所有集合一股脑塞进同一个 url-test 组——两家服务商的节点混在一起自动择优,结果往往是流量全压在延迟最低的那一家上,另一家的额度白白闲置,而且某家整体故障时切换体感很差。更好的结构是「每份订阅各自一个自动测速组,再用一个 fallback 把它们串起来」:主订阅优先,主订阅整体不可用时自动落到备用订阅。这样既保留了自动择优,也把「换服务商」这件事降级成组内切换。
filter 字段在这里很有用:同一个集合可以按关键词派生出多个地区组,例如用正则筛出节点名里带特定地区标识的成员组成一个组,规则里再把特定业务指向该地区组。要留意的是筛选依赖节点命名,服务商一旦改名就会出现空组——组内没有可用成员时,内核会在启动时报错或让该组不可选,遇到这种情况先去看订阅里的节点名是否变了。
覆写的调试与冲突
覆写写错了同样有典型症状:改动完全没生效,或者订阅里原有的某个键整块消失。前者多半是键名层级放错了位置(比如把 fake-ip-filter 写在顶层而不是 dns 之下),后者则是把「整键替换」当成了「局部合并」——只想加一条 fake-ip-filter,结果写了个只含一条目的 dns 键,把订阅里其余 DNS 配置全部覆盖掉了。判断方法很简单:在客户端里查看最终生成的运行时配置,那份配置才是内核真正加载的内容,和你脑子里以为的样子对照一遍,冲突点立刻现形。养成「改覆写 → 看运行时配置 → 再看日志」的习惯,能省下大量猜测时间。
外部控制与面板
内核运行时的一切状态——节点延迟、活动连接、实时日志、当前模式——都通过一套 RESTful API 对外暴露,这就是外部控制接口。客户端里的连接列表、Web 面板、命令行查询,底层全是同一套 API。
external-controller: 127.0.0.1:9090
secret: "your-secret"
external-ui: ./ui
external-controller 声明监听地址与端口;secret 是访问令牌,所有请求需在头部携带;external-ui 指向一个静态面板目录,内核会把它托管在同端口的 /ui 路径下,浏览器直接访问即可。主流 Web 面板都是纯前端项目,解压进该目录就能用。
用 curl 直接对话内核
# 查看全部策略组与节点状态
curl -H "Authorization: Bearer your-secret" \
http://127.0.0.1:9090/proxies
# 切换「节点选择」组的当前出口
curl -X PUT -H "Authorization: Bearer your-secret" \
-d '{"name": "自动测速"}' \
http://127.0.0.1:9090/proxies/节点选择
# 实时日志流 / 活动连接
curl -H "Authorization: Bearer your-secret" \
http://127.0.0.1:9090/logs
curl -H "Authorization: Bearer your-secret" \
http://127.0.0.1:9090/connections
常用端点四个就够记:/proxies(组与节点)、/connections(活动连接,排查"这条流量走了哪个出口"最直接)、/logs(实时日志流)、/configs(读取与热更新运行配置)。写脚本做自动化——比如网络切换后自动换组——也是围绕这几个端点。
安全底线:secret 必须设置且足够复杂;监听地址默认保持 127.0.0.1,确有局域网管理需求再改成局域网地址——把控制接口无鉴权地暴露在 0.0.0.0 上,等于把整台设备的流量调度权交给同网段的任何人。
iOS 场景补一句:Clash Plus 的内置面板在客户端内部消费同一套接口,普通使用不需要手动配置本节内容;真正需要外部控制的,是桌面端调试与路由器无头部署两类场景。
把 API 当成排查工具
外部控制接口最大的价值不是自动化,而是排查。当你怀疑某条流量走错出口时,/connections 返回的每条记录都带着完整链路信息:目标域名或 IP、命中的规则、最终使用的策略组与节点、上下行字节数、建连时间。把它和日志对照,「规则为什么没命中」这类问题通常一眼就能定位——要么命中了另一条更靠前的规则,要么根本没拿到域名(回到第五章的嗅探)。/proxies 则能一次性看到所有组的当前选择与各成员延迟,判断是节点问题还是分流问题时非常高效。
/configs 支持热更新部分运行时配置,比如切换 rule / global / direct 模式,或触发一次配置重载。调试阶段用它可以省掉反复重启客户端的时间,但要清楚一点:通过 API 做的临时改动不会写回配置文件,重启即失效,真正要保留的改动仍然得落到配置或覆写里。
面板选择与访问安全
Web 面板本身是纯静态前端,连接的是你填的控制器地址与令牌,因此「用哪个面板」不影响内核行为,只影响操作体验。用 external-ui 让内核自己托管面板的好处是同源访问、不用额外服务;缺点是升级面板要手动替换目录。若选择在线面板,请确认它以 HTTPS 提供且你信任其来源——填进去的令牌等同于设备流量的控制权。
最后重申一次安全边界,因为这是全篇唯一可能造成实质风险的配置项:控制器监听地址、令牌复杂度、以及是否经过公网,三者只要有一项失守,任何能访问到该端口的人都可以读取你的全部连接记录、切换出口、乃至替换配置。确有远程管理需求时,正确做法是通过 SSH 隧道或内网穿透把端口映射到可信通道上,而不是直接把 external-controller 改成 0.0.0.0 并在路由器上做端口转发。到这里八章内容结束,配置层面的问题基本都能在本页找到入手点;剩下的就是按主题逐项验证,把手册变成你自己那份稳定配置。