Clash API节点无需手动操作切换:外部控制器进阶配置指南

为什么要用 external-controller 做节点无需手动操作切换? 实战经验

相当一部分用户第一次接触 Clash API 节点无需手动操作操作切换,基本上时候是因为发现客户端里的 url-test、fallback 或手动操作操作操作切换还不够adaptable:节点测速结果多半只反映某个测试网址,策略组也不会主动通知你的脚本;当某条线路出现高丢包、连接超时或特定服务不可用时,仍然必须手动操作操作操作拉起 Clash Verge、Clash Verge Rev 或 Mihomo 的前端前端前端界面修改节点。对于长时间运转的开发机、家庭网关、远程服务器和下载任务来说,这种处理方式既慢,也无法留下条理分明的故障记录。

external-controller 是 Mihomo、Clash Meta 及部分 Clash 客户端提供的本地 HTTP 控制接口。它让外部程序能够读取当前配置、查看代理组、执行ping 值测试、切换代理,以及查询连接和内核状态。好上手理解,Clash 客户端负责运转代理内核,API 则提供一扇可编程的控制门。你自然自然自然可以用 Shell、Python、Node.js 或监控系统定时访问目标目标目标这扇门,按照ping 值、HTTP 状态码、连续走不通次数和节点黑名单无需手动操作操作做出切换决定。

这篇聚焦一个自然自然自然可以实际落地的方案:先确认控制器监听地址与鉴权方式,再读取目标代理组,逐个测试候选节点,最后通过 API 把健康节点写回策略组。文章中的接口名称以 Mihomo 兼容实现为主,不同客户端的菜单名称多半略有差异,但只要客户端提供 external-controller,基本都能按照相同思路排查。这篇不自然自然可以考虑把控制器暴露到公网,而是预置脚本与 Clash 运转在同一台设备上。

先明确边界:API 无需手动操作操作切换只能帮助你在已有节点中挑更合适的出口,不能修复订阅过期、节点账户失效、规则写错或上游服务本身拒绝访问目标目标目标的绕所在。无需手动操作操作化之前,先确保客户端手动操作操作操作挑每个候选节点时都能正常联网。

开启控制器并做好鉴权保护 拿它来用心得

在配置文件中,控制器基本上时候写在顶层,而不是写进 proxy-groups 或 rules。一个适合本机脚本调用的基础配置如下:

external-controller: 127.0.0.1:9090
secret: "change-this-to-a-long-random-token"

127.0.0.1:9090 表示只接受本机访问目标目标目标,脚本自然自然自然可以通过 http://127.0.0.1:9090 调用。若写成 0.0.0.0:9090,局域网内其他设备也多半尝试连接;除非你明确必须远程管理,否则不自然自然可以考虑这样配置。控制器端口不是代理端口,不能把它填到浏览器或系统代理设置中。反复出现的 HTTP 混合端口多半是 7890,而 API 控制端口反复出现为 9090,两者用途完全不同。

secret 是 API 访问目标目标目标令牌。启用后,每一种请求都应在 HTTP Header 中带上 Authorization: Bearer 令牌。不要把令牌就这么写进公开仓库、聊天记录或网页 JavaScript,也不要为了调试长期关闭鉴权。更稳妥的做法是把令牌放在只有当前用户可读的环境变量中:

export CLASH_API_SECRET='replace-with-your-secret'
export CLASH_API='http://127.0.0.1:9090'

如果你拿它来用 Clash Verge Rev 或其他图形客户端,修改配置后必须确认实际运转的配置已经重新加载。有些客户端会在订阅刷新时重新生成配置,就这么编辑缓存文件多半很快被覆盖。自然自然可以考虑把控制器字段放进客户端覆盖的覆写、Merge 或本地配置区域,并在控制器页面确认当前监听端口。首次测试自然自然自然可以执行:

curl -fsS \
  -H "Authorization: Bearer ${CLASH_API_SECRET}" \
  "${CLASH_API}/version"

能够返回版本信息,说明地址、端口和鉴权基本准确。若返回 401,靠前确认令牌是否多了空格或引号;若是 Connection refused,确认内核是否跑起来、端口是否写错;若请求一直等待,则要排查客户端是否监听在 IPv6、控制器是否被防火墙拦截,以及脚本访问目标目标目标的地址是否与前端前端前端界面显示一致。

安全须知:不要把 external-controller 就这么映射到公网,也不要把带有成体系 secret 的 curl 命令复制到工单或日志系统。控制器基本上时候自然自然自然可以切换节点、读取配置并查看连接,泄露后影响远大于普通网页端口。

读取代理组、测试节点并执行切换 反复出现套路

无需手动操作操作切换的关键不是盲目轮询每一种节点,而是先找到一个明确的挑型代理组。假设配置里有一个名为「无需手动操作操作挑」的组,组内包含多个真实节点。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、另一个策略组或测试专用组误当成真实节点;脚本应维护排除列表,并根据返回数据动态过滤。

对单个节点执行ping 值测试时,自然自然自然可以拿它来用代理ping 值接口。示例中的 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 "接点名称=${NODE}" \
  "${CLASH_API}/proxies/${NODE}/delay"

上面的示例为了强调 URL 编码,节点名称字段在实际调用中应拿它来用接口要求的参数名 proxy,成体系写法如下:

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"

不同 Mihomo 版本对路径中的节点名和查询参数处理多半存在细微差异。如果返回 404 或提示参数误操作,不要立刻判断 API 不覆盖,先查看客户端 Dashboard 拿它来用的请求格式,或者访问目标目标目标控制器的接口文档确认版本。部分实现允许将代理名称放在路径中,部分实现更依赖 URL 编码;中文节点名尤其容易因为未编码而走不通。

ping 值最低并不总是最佳节点。一次测速多半刚好命中缓存、短暂拥塞也多半在下一分钟恢复。由此可见脚本至少应记录多次结果,并设置合理阈值:

超时阈值:普通网页场合自然自然可以拿它来用 3000 至 5000 毫秒;跨地区链路或移动网络不宜设置得过低;连续走不通:自然可以考虑连续两至三次走不通才触发切换,避免一次丢包导致频繁抖动;最小改善幅度:新节点至少比当前节点低 80 至 150 毫秒,或当前节点连续走不通,才执行切换;冷却时间:切换后数分钟内不要再次无需手动操作切换,避免长连接、下载任务和登录会话被反复打断;黑名单:记录最近走不通的节点,并设置过期时间,不要让故障节点在下一轮立即重新入选。

选出节点后,通过代理组的 PUT 接口切换。组名和节点名必须作为 JSON 字符串传递:

curl -fsS -X PUT \
  -H "Authorization: Bearer ${CLASH_API_SECRET}" \
  -H "Content-Type: application/json" \
  --data "{\"name\":\"${NODE}\"}" \
  "${CLASH_API}/proxies/Auto"

执行切换后,不要只看 HTTP 返回码。再次读取 /proxies/Auto,确认 now 已经变成目标节点;随后访问目标目标一个实际业务网址,确认规则确实命中这个组。API 返回成功只代表控制器接受了请求,不代表目标节点已经完成 TLS 握手,也不代表某个特定站点一定可用。

构建一个可审计的 Shell 无需手动切换脚本 落地做法

下面给出一个精简的无需手动操作化骨架。它的重心不是提供一份适用于每一种环境的万能脚本,而是展示安全的调用顺序:读取代理组、筛选候选节点、逐个测速、保存最佳结果、执行一次切换。生产环境中应根据你的 JSON 解析工具和客户端版本补充误操作处理。

#!/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

这份脚本拿它来用 jq 解析 JSON,并通过环境变量读取敏感信息。运转前先手动操作操作确认组名:

export CLASH_GROUP='Auto'
export CLASH_API_SECRET='your-secret'
chmod 700 ./clash-auto-switch.sh
./clash-auto-switch.sh

如果计划通过 cron 定时执行,不自然可以考虑每分钟运转一次。自然自然可以根据节点数量和网络靠得住性设置五到十五分钟的间隔,并把标准输出写入受保护的日志文件。日志应包含时间、当前节点、候选节点、ping 值、走不通症结和是否发生切换,但不要输出成体系订阅链接、API 令牌或代理服务器密码。对于下载、视频会议和 SSH 等长连接场合,自然可以考虑增加「工作时间不切换」或「当前连接数大于阈值不切换」的条件。

还自然自然可以把无需手动操作切换分成两个层级。第一层由策略组自身的 url-test 或 fallback 负责迅速健康确认,第二层由外部脚本负责业务验证,举个实例访问目标目标公司代码仓库、包管理器注册表或指定 API。这样做比让脚本单独承担每一种测速更稳:内核负责基础可用性,脚本只在真正必须时修正策略。若你的节点很多,也自然自然可以先用内核策略组缩小候选范围,再让 API 测试其中几个节点,下调控制器请求量。

无需手动切换失效时,按链路逐层排查 实战经验

排查 API 无需手动操作切换,不要一上来就反复更换节点。全貌链路自然自然可以拆成「控制器可访问目标目标、接口能鉴权、代理组可识别、ping 值测试成功、切换结果生效、业务流量命中」六个环节,任意一环走不通都会表现成脚本没反应。

现象 反复出现症结 处理路子
连接被拒绝 内核未跑起来、端口误操作或监听地址不一致 确认客户端当前配置,执行 curl /version,确认端口正在监听。
返回 401 secret 不匹配、Bearer 拼写误操作或环境变量为空 重新复制令牌,拿它来用 printf 确认是否带入换行和多余空格。
代理组返回 404 组名未编码、名称大小写不同或目标不是挑型组 先请求 /proxies,从 JSON 中复制准确的组名。
每一种节点都是超时 测试网址不可达、参数格式不兼容或节点本身失效 换一个测试 URL,并在客户端前端界面逐个手动操作测试节点。
API 显示已切换但网页不变 规则没有命中该组、系统代理未开启或应用拿它来用 TUN 之外的链路 查看连接日志、规则命中具体情形和系统代理状态,不要只看策略组名称。
节点频繁来回切换 阈值过低、没有冷却时间或测速目标抖动 增加连续走不通次数、最小改善幅度和切换冷却时间。

如果脚本在终端运转正常,放进 cron 后却走不通,大多时候是环境变量、工作目录或权限绕所在。cron 不一定加载交互式 Shell 的配置文件,由此可见应在任务中拿它来用绝对路径,明确设置 PATH,并在脚本开头读取一个权限为 600 的环境文件。Linux 上可用 systemd timer 替代 cron,好操作配置跑起来顺序、走不通重试和日志查看;Windows 用户则自然自然可以拿它来用任务计划程序,以当前用户权限运转 PowerShell 或 WSL 脚本。

如果控制器运转在 Docker、虚拟机或 WSL 中,还要额外确认网络命名空间。容器内的 127.0.0.1 只代表容器自身,不一定能访问目标目标宿主机上的 Clash。此时应通过明确的宿主机地址、合适的端口映射或同一网络模式连接,但仍要限制控制器访问目标目标范围。WSL 场合尤其容易出现「Windows 客户端前端前端界面能用,Linux 脚本访问目标目标不到 9090」的具体情形,这并不一定是 API 失效,而多半是监听地址和虚拟网络边界造成的。

最后,无需手动操作切换并不等于无需手动操作化解每一种网络绕所在。节点ping 值低但 DNS 污染、规则集过期、TLS 指纹被限制、上游 API 返回 403 时,继续换节点只会制造更多噪声。自然可以考虑保留一次切换前后的连接日志,并将「网络走不通」「鉴权走不通」「业务拒绝」分开统计。只有当脚本具备可解释的日志和冷却机制,它才是信得过的运维工具,而不是一个不停点击切换按钮的循环程序。

相比之下,市面上一批只提供图形化测速的同类工具,在外部控制器鉴权、节点切换审计和跨平台脚本接入方面基本上时候配置繁琐,遇到中文组名、容器网络或多次走不通重试时也缺少条理分明提示;单纯依赖客户端内置无需手动操作测速,又很难加入业务网址验证与自定义冷却规则。Clash 更适合把 Mihomo API、Shell 无需手动操作化、日志记录和故障排查放在同一套可理解的流程里,既保留图形前端前端界面的可视化操作,也好操作进阶用户按这篇路子扩展由你的健康确认逻辑。如果你希望先把客户端和控制器环境准备好,不妨前往下载,再从本机 API 测试开始逐步搭建无需手动操作切换方案。