Clash API节点无人值守切换:外部控制器进阶配置指南
为什么要用 external-controller 做节点无人值守切换? 一文看懂
文章中的接口名称以 Mihomo 兼容实现为主,不同客户端的菜单名称也许略有差异,但只要客户端提供 external-controller,基本都能按照相同思路排查。这篇文章不推荐把控制器暴露到公网,而是内置脚本与 Clash 跑起来在同一台设备上。
不少运用者第一次接触 Clash API 节点无人值守切换,一般是因为发现客户端里的 url-test、fallback 或手工切换还不够弹性:节点测速结果也许只反映某个测试网址,策略组也不会主动通知你的脚本;
当某条线路出现高丢包、连接超时或特定服务不可用时,仍然得手工开启 Clash Verge、Clash Verge Rev 或 Mihomo 的操作操作操作界面修改节点。对于长时间跑起来的开发机、家庭网关、远程服务器和下载任务来说,这种处理方式既慢,也无法留下明白的故障记录。
external-controller 是 Mihomo、Clash Meta 及部分 Clash 客户端提供的本地 HTTP 控制接口。它让外部程序能够读取当前配置、查看代理组、执行时延测试、切换代理,以及查询连接和内核状态。
先看这一点:容易理解,Clash 客户端负责跑起来代理内核,API 则提供一扇可编程的控制门。你能够用 Shell、Python、Node.js 或监控系统定时连通这扇门,按照时延、HTTP 状态码、连续不成功次数和节点黑名单无人值守做出切换决定。
这篇文章聚焦一个能够实际落地的方案:先确认控制器监听地址与鉴权方式,再读取目标代理组,逐个测试候选节点,最后通过 API 把健康节点写回策略组。
先明确边界:API 无人值守切换只能帮助你在已有节点中挑选更合适的出口,不能修复订阅过期、节点账户失效、规则写错或上游服务本身拒绝连通的难点。无人值守化之前,先确保客户端手工挑选每个候选节点时都能正常联网。
开启控制器并做好鉴权保护 核心要点
能够返回版本信息,说明地址、端口和鉴权基本无误。若返回 401,首位核对令牌是否多了空格或引号;若是 Connection refused,核对内核是否拉起、端口是否写错;若请求一直等待,则要排查客户端是否监听在 IPv6、控制器是否被防火墙拦截,以及脚本连通的地址是否与操作操作操作界面显示一致。
external-controller: 127.0.0.1:9090
secret: "change-this-to-a-long-random-token"
在配置文件中,控制器一般写在顶层,而不是写进 proxy-groups 或 rules。一个适合本机脚本调用的基础配置如下:
补充说明:127.0.0.1:9090 表示只接受本机连通,脚本能够通过 http://127.0.0.1:9090 调用。若写成 0.0.0.0:9090,局域网内其他设备也也许尝试连接;除非你明确得远程管理,否则不推荐这样配置。控制器端口不是代理端口,不能把它填到浏览器或系统代理设置中。高频的 HTTP 混合端口也许是 7890,而 API 控制端口高频为 9090,两者用途完全不同。
export CLASH_API_SECRET='replace-with-your-secret'
export CLASH_API='http://127.0.0.1:9090'
secret 是 API 连通令牌。启用后,全部请求都应在 HTTP Header 中带上 Authorization: Bearer 令牌。不要把令牌干脆写进公开仓库、聊天记录或网页 JavaScript,也不要为了调试长期关闭鉴权。更稳妥的做法是把令牌放在只有当前用户可读的环境变量中:
curl -fsS \
-H "Authorization: Bearer ${CLASH_API_SECRET}" \
"${CLASH_API}/version"
如果你运用 Clash Verge Rev 或其他图形客户端,修改配置后得确认实际跑起来的配置已经重新加载。有些客户端会在订阅刷新时重新生成配置,干脆编辑缓存文件也许很快被覆盖。推荐把控制器字段放进客户端兼容的覆写、Merge 或本地配置区域,并在控制器页面确认当前监听端口。首次测试能够执行:
风险提示:不要把 external-controller 干脆映射到公网,也不要把带有齐全 secret 的 curl 命令复制到工单或日志系统。控制器一般能够切换节点、读取配置并查看连接,泄露后影响远大于普通网页端口。
读取代理组、测试节点并执行切换 逐条拆解
执行切换后,不要只看 HTTP 返回码。再次读取 /proxies/Auto,确认 now 已经变成目标节点;随后连通一个实际业务网址,确认规则确实命中这个组。API 返回成功只代表控制器接受了请求,不代表目标节点已经完成 TLS 握手,也不代表某个特定站点一定可用。
换个角度:无人值守切换的关键不是盲目轮询全部节点,而是先找到一个明确的挑选型代理组。假设配置里有一个名为「无人值守挑选」的组,组内包含多个真实节点。API 路径中的组名得进行 URL 编码,中文名称、空格和特殊字符不能干脆拼接。为了减少脚本复杂度,推荐将无人值守化目标命名为不含空格的英文或短中文名称,比如 Auto 或 Proxy。
curl -fsS \
-H "Authorization: Bearer ${CLASH_API_SECRET}" \
"${CLASH_API}/proxies"
查看全部代理组能够请求:
返回的 JSON 中,proxies 对象包含各个代理组和节点。挑选型代理组一般有 type: "Selector",并带有 all、now 等字段。now 是当前正在运用的成员,all 是能够切换的候选列表。不要把 DIRECT、REJECT、另一个策略组或测试专用组误当成真实节点;脚本应维护排除列表,并根据返回数据动态过滤。
curl -fsS -G \
-H "Authorization: Bearer ${CLASH_API_SECRET}" \
--data-urlencode "url=https://www.gstatic.com/generate_204" \
--data-urlencode "timeout=5000" \
--data-urlencode "接点名称=${NODE}" \
"${CLASH_API}/proxies/${NODE}/delay"
具体来说:对单个节点执行时延测试时,能够运用代理时延接口。示例中的 URL 只是测试目标,实际项目应挑选稳健、响应素材容易且符合你所在网络环境的地址:
curl -fsS -G \
-H "Authorization: Bearer ${CLASH_API_SECRET}" \
--data-urlencode "url=https://www.gstatic.com/generate_204" \
--data-urlencode "timeout=5000" \
--data-urlencode "proxy=${NODE}" \
"${CLASH_API}/proxies/${NODE}/delay"
上面的示例为了强调 URL 编码,节点名称字段在实际调用中应运用接口要求的参数名 proxy,齐全写法如下:
不同 Mihomo 版本对路径中的节点名和查询参数处理也许存在细微差异。如果返回 404 或提示参数失误,不要立刻判断 API 不兼容,先查看客户端 Dashboard 运用的请求格式,或者连通控制器的接口文档确认版本。部分实现允许将代理名称放在路径中,部分实现更依赖 URL 编码;中文节点名尤其容易因为未编码而不成功。
- 超时阈值:普通网页状态能够运用 3000 至 5000 毫秒;跨地区链路或移动网络不宜设置得过低。
- 连续不成功:推荐连续两至三次不成功才触发切换,避免一次丢包导致频繁抖动。
- 最小改善幅度:新节点至少比当前节点低 80 至 150 毫秒,或当前节点连续不成功,才执行切换。
- 冷却时间:切换后数分钟内不要再次无人值守切换,避免长连接、下载任务和登录会话被反复打断。
- 黑名单:记录最近不成功的节点,并设置过期时间,不要让故障节点在下一轮立即重新入选。
顺带一提:时延最低并不总是最佳节点。一次测速也许刚好命中缓存、短暂拥塞也也许在下一分钟恢复。所以脚本至少应记录多次结果,并设置合理阈值:
curl -fsS -X PUT \
-H "Authorization: Bearer ${CLASH_API_SECRET}" \
-H "Content-Type: application/json" \
--data "{\"name\":\"${NODE}\"}" \
"${CLASH_API}/proxies/Auto"
选出节点后,通过代理组的 PUT 接口切换。组名和节点名必须作为 JSON 字符串传递:
构建一个可审计的 Shell 无人值守切换脚本 底层逻辑
这样做比让脚本单独承担全部测速更稳:内核负责基础可用性,脚本只在真正得时修正策略。若你的节点很多,也能够先用内核策略组缩小候选范围,再让 API 测试其中几个节点,压低控制器请求量。
#!/usr/bin/env bash
set -Eeuo pipefail
API="${CLASH_API:-http://127.0.0.1:9090}"
SECRET="${CLASH_API_SECRET:?CLASH_API_SECRET is required}"
GROUP="${CLASH_GROUP:-Auto}"
TEST_URL="${CLASH_TEST_URL:-https://www.gstatic.com/generate_204}"
TIMEOUT="${CLASH_TIMEOUT:-5000}"
api_get() {
curl -fsS --max-time 8 \
-H "Authorization: Bearer ${SECRET}" "$1"
}
api_put() {
curl -fsS --max-time 8 -X PUT \
-H "Authorization: Bearer ${SECRET}" \
-H "Content-Type: application/json" \
--data "$2" "$1"
}
group_json="$(api_get "${API}/proxies/${GROUP}")"
current="$(printf '%s' "$group_json" | jq -r '.now')"
best=""
best_delay=999999
while IFS= read -r node; do
[ -z "$node" ] && continue
case "$node" in
DIRECT|REJECT|GLOBAL|URL-TEST|Fallback) continue ;;
esac
result="$(curl -fsS -G --max-time 8 \
-H "Authorization: Bearer ${SECRET}" \
--data-urlencode "url=${TEST_URL}" \
--data-urlencode "timeout=${TIMEOUT}" \
--data-urlencode "proxy=${node}" \
"${API}/proxies/$(printf '%s' "$node" | jq -sRr @uri)/delay" \
|| true)"
delay="$(printf '%s' "$result" | jq -r '.delay // empty' 2>/dev/null || true)"
if [[ "$delay" =~ ^[0-9]+$ ]] && [ "$delay" -lt "$best_delay" ]; then
best="$node"
best_delay="$delay"
fi
done < <(printf '%s' "$group_json" | jq -r '.all[]')
if [ -n "$best" ] && [ "$best" != "$current" ]; then
api_put "${API}/proxies/$(printf '%s' "$GROUP" | jq -sRr @uri)" \
"$(jq -n --arg name "$best" '{name: $name}')"
printf '%s switched %s -> %s (%sms)\n' \
"$(date -Is)" "$current" "$best" "$best_delay"
else
printf '%s keep %s; no better healthy node\n' "$(date -Is)" "$current"
fi
下面给出一个精简的无人值守化骨架。它的要点不是提供一份适用于全部环境的万能脚本,而是展示安全的调用顺序:读取代理组、筛选候选节点、逐个测速、保存最佳结果、执行一次切换。生产环境中应根据你的 JSON 解析工具和客户端版本补充失误处理。
export CLASH_GROUP='Auto'
export CLASH_API_SECRET='your-secret'
chmod 700 ./clash-auto-switch.sh
./clash-auto-switch.sh
先看这一点:这份脚本运用 jq 解析 JSON,并通过环境变量读取敏感信息。跑起来前先手工确认组名:
如果计划通过 cron 定时执行,不推荐每分钟跑起来一次。能够根据节点数量和网络稳健性设置五到十五分钟的间隔,并把标准输出写入受保护的日志文件。
日志应包含时间、当前节点、候选节点、时延、不成功缘由和是否发生切换,但不要输出齐全订阅链接、API 令牌或代理服务器密码。对于下载、视频会议和 SSH 等长连接状态,推荐增加「工作时间不切换」或「当前连接数突破阈值不切换」的条件。
补充说明:还能够把无人值守切换分成两个层级。第一层由策略组自身的 url-test 或 fallback 负责高速健康核对,第二层由外部脚本负责业务验证,比如连通公司代码仓库、包管理器注册表或指定 API。
无人值守切换失效时,按链路逐层排查 一文看懂
Clash 更适合把 Mihomo API、Shell 无人值守化、日志记录和故障排查放在同一套可理解的流程里,既保留图形操作操作界面的一眼看懂操作,也省事进阶用户按这篇文章做法扩展亲手的健康核对逻辑。如果你希望先把客户端和控制器环境准备好,不妨前往下载,再从本机 API 测试开始逐步搭建无人值守切换方案。
| 现象 | 高频缘由 | 处理做法 |
|---|---|---|
| 连接被拒绝 | 内核未拉起、端口失误或监听地址不一致 | 核对客户端当前配置,执行 curl /version,确认端口正在监听。 |
| 返回 401 | secret 不匹配、Bearer 拼写失误或环境变量为空 | 重新复制令牌,运用 printf 核对是否带入换行和多余空格。 |
| 代理组返回 404 | 组名未编码、名称大小写不同或目标不是挑选型组 | 先请求 /proxies,从 JSON 中复制准确的组名。 |
| 全部节点都是超时 | 测试网址不可达、参数格式不兼容或节点本身失效 | 换一个测试 URL,并在客户端操作界面逐个手工测试节点。 |
| API 显示已切换但网页不变 | 规则没有命中该组、系统代理未开启或应用运用 TUN 之外的链路 | 查看连接日志、规则命中状态和系统代理状态,不要只看策略组名称。 |
| 节点频繁来回切换 | 阈值过低、没有冷却时间或测速目标抖动 | 增加连续不成功次数、最小改善幅度和切换冷却时间。 |
排查 API 无人值守切换,不要一上来就反复更换节点。整套链路能够拆成「控制器可连通、接口能鉴权、代理组可识别、时延测试成功、切换结果生效、业务流量命中」六个环节,无论哪种一环不成功都会表现成脚本没反应。
如果脚本在终端跑起来正常,放进 cron 后却不成功,通常是环境变量、工作目录或权限难点。cron 不一定加载交互式 Shell 的配置文件,所以应在任务中运用绝对路径,明确设置 PATH,并在脚本开头读取一个权限为 600 的环境文件。
换个角度:Linux 上可用 systemd timer 替代 cron,省事配置拉起顺序、不成功重试和日志查看;Windows 用户则能够运用任务计划程序,以当前用户权限跑起来 PowerShell 或 WSL 脚本。
如果控制器跑起来在 Docker、虚拟机或 WSL 中,还要额外核对网络命名空间。容器内的 127.0.0.1 只代表容器自身,不一定能连通宿主机上的 Clash。此时应通过明确的宿主机地址、合适的端口映射或同一网络模式连接,但仍要限制控制器连通范围。
WSL 状态尤其容易出现「Windows 客户端操作操作界面能用,Linux 脚本连通不到 9090」的状态,这并不一定是 API 失效,而也许是监听地址和虚拟网络边界造成的。
具体来说:最后,无人值守切换并不等于无人值守处理全部网络难点。节点时延低但 DNS 污染、规则集过期、TLS 指纹被限制、上游 API 返回 403 时,继续换节点只会制造更多噪声。推荐保留一次切换前后的连接日志,并将「网络不成功」「鉴权不成功」「业务拒绝」分开统计。只有当脚本具备可解释的日志和冷却机制,它才是靠谱的运维工具,而不是一个不停点击切换按钮的循环程序。
相比之下,市面上若干只提供图形化测速的同类工具,在外部控制器鉴权、节点切换审计和跨平台脚本接入方面一般配置繁琐,遇到中文组名、容器网络或多次不成功重试时也缺少明白提示;单纯依赖客户端内置无人值守测速,又很难加入业务网址验证与自定义冷却规则。