Clash 的 external-controller 不只是 Dashboard 的连接入口,它本质上是一套可以被脚本调用的 HTTP 控制接口。通过这套 API,程序能够读取策略组状态、测试节点延迟、切换当前节点,甚至在发现连续故障后执行自动恢复。对于节点数量较多、网络质量变化明显,或者需要在服务器上长期运行 Clash 的用户来说,将人工点击切换升级为自动化运维,可以显著降低断网时间。本文以 Clash/Mihomo 为基础,介绍 external-controller 的安全配置、节点健康检查、API 调用方式,以及一套可以按需改造的自动切换脚本。

external-controller 的工作原理与安全边界

Clash 启动后会同时运行代理服务和控制服务。代理服务负责接收浏览器、系统或 TUN 转发过来的网络请求;控制服务则监听一个单独的 HTTP 地址,供 Dashboard 或自动化程序访问。两者使用同一个 Clash 进程,但职责完全不同。控制服务可以读取当前配置、查询连接、获取策略组信息,也可以修改策略组正在使用的节点。

在配置文件中,最基本的启用方式如下。建议优先绑定本机地址,只有确实需要远程管理时才开放局域网访问。

启用 External Controller
# 仅允许本机访问,适合桌面端脚本
external-controller: 127.0.0.1:9090

# API 认证密钥,生产环境不要留空
secret: replace-with-a-long-random-secret

# 如需局域网管理,可改为 0.0.0.0,但必须配合防火墙
# external-controller: 0.0.0.0:9090

当配置了 secret 后,调用 API 时必须在 HTTP 请求头中加入 Authorization: Bearer 密钥。如果将监听地址设置为 0.0.0.0 而没有设置密钥,局域网中的其他设备就可能直接切换节点、读取代理信息,甚至影响正在进行的连接。即使设置了密钥,也不建议把 9090 端口暴露到公网。

  • 桌面端本机脚本:使用 127.0.0.1:9090,通过本地回环接口访问。
  • 路由器或服务器:绑定内网地址,使用防火墙限制来源 IP,只允许管理设备访问。
  • 跨公网管理:优先通过 WireGuard、Tailscale 或 SSH 隧道进入内网,不要直接开放控制端口。
  • 密钥管理:不要把 secret 写入公开仓库,脚本中应使用环境变量或单独的权限文件。
安全提醒:external-controller 拥有修改运行状态的权限。若 API 被未授权访问,攻击者可以查看节点名称和连接信息,也可能将策略组切换到不可信节点。开放局域网监听时,必须同时启用强密钥、系统防火墙和最小化访问范围。

核心 API 与节点健康检查方法

自动切换的关键不是简单地“发现延迟高就换节点”,而是先确定要监控哪个策略组,再使用稳定的测试地址判断节点是否可用。Mihomo/Clash 常用的几个接口如下:

用途请求方式接口说明
读取所有策略组GET/proxies返回策略组、当前节点和节点列表
读取单个策略组GET/proxies/{名称}查看某个组当前选择和可用成员
测试节点延迟GET/proxies/{名称}/delay需要传入 url 与 timeout 参数
切换策略组PUT/proxies/{名称}请求体为 JSON 格式的 proxy 名称
读取当前连接GET/connections辅助判断是否存在活跃连接和异常流量

例如,查看名为“自动选择”的策略组,可以执行以下请求。策略组名称需要进行 URL 编码,包含中文、空格或特殊字符时尤其要注意。

查询策略组状态
# 使用 curl 查询策略组
curl -s \
  -H "Authorization: Bearer ${CLASH_SECRET}" \
  "http://127.0.0.1:9090/proxies/$(python3 -c 'import urllib.parse; print(urllib.parse.quote("自动选择"))')"

节点延迟测试通常通过 /proxies/{proxy}/delay 完成,并传入一个返回 204 或 200 的稳定 URL。测试 URL 不宜使用体积很大的网页,否则测到的结果会混入下载时间;也不建议只使用一个容易被本地网络缓存的地址。可以选择轻量级的 HTTPS 探测地址,并将超时时间设置在 3000 至 8000 毫秒之间。

测试指定节点延迟
# 节点名称为“节点-HK-01”时,先进行 URL 编码
curl -s \
  -H "Authorization: Bearer ${CLASH_SECRET}" \
  "http://127.0.0.1:9090/proxies/%E8%8A%82%E7%82%B9-HK-01/delay?url=https%3A%2F%2Fwww.gstatic.com%2Fgenerate_204&timeout=5000"

健康检查应该至少考虑三个结果:请求成功且延迟合理、请求超时或连接失败、请求成功但延迟明显高于阈值。不要因为一次超时就立刻切换,否则短暂丢包会造成策略组频繁抖动。更稳妥的做法是保存连续失败次数,例如连续两次或三次失败后才将节点标记为不健康;节点恢复时也可以连续成功两次后再恢复使用。

通过 HTTP API 执行自动切换

切换策略组使用 PUT 请求,JSON 请求体中的字段名是 name。需要注意,切换的是策略组当前选择的代理,而不是直接修改 YAML 文件,因此切换通常立即生效,但是否在 Clash 重启后保留,取决于客户端的配置持久化行为。若用户手动选择了节点,自动脚本也可能覆盖这个选择,因此建议专门建立一个“自动故障转移”策略组。

切换当前策略组节点
# 将“自动选择”切换到“节点-JP-01”
curl -X PUT \
  -H "Authorization: Bearer ${CLASH_SECRET}" \
  -H "Content-Type: application/json" \
  -d '{"name":"节点-JP-01"}' \
  "http://127.0.0.1:9090/proxies/%E8%87%AA%E5%8A%A8%E9%80%89%E6%8B%A9"

下面是一份 Python 示例,它会读取指定策略组中的节点,逐一进行延迟测试,并在当前节点连续失败时切换到第一个可用节点。示例使用标准库,不依赖第三方包,适合放在桌面电脑、Linux 服务器或路由器环境中运行。实际使用时应根据节点数量、网络质量和 API 版本调整参数。

Python 自动切换脚本
import json
import os
import time
import urllib.parse
import urllib.request
import urllib.error

API = os.getenv("CLASH_API", "http://127.0.0.1:9090")
SECRET = os.environ["CLASH_SECRET"]
GROUP = "自动选择"
TEST_URL = "https://www.gstatic.com/generate_204"
TIMEOUT = 5000
FAIL_LIMIT = 2

headers = {
    "Authorization": "Bearer " + SECRET,
    "Content-Type": "application/json",
}

def request(path, method="GET", data=None):
    body = None if data is None else json.dumps(data).encode("utf-8")
    req = urllib.request.Request(
        API + path, headers=headers, method=method, data=body
    )
    with urllib.request.urlopen(req, timeout=8) as response:
        return json.loads(response.read().decode("utf-8"))

def quote(value):
    return urllib.parse.quote(value, safe="")

def test_proxy(proxy):
    path = "/proxies/" + quote(proxy) + "/delay?url=" + quote(TEST_URL)
    path += "&timeout=" + str(TIMEOUT)
    try:
        result = request(path)
        return int(result.get("delay", 0))
    except (urllib.error.URLError, ValueError, TimeoutError):
        return None

def switch_to(proxy):
    request("/proxies/" + quote(GROUP), "PUT", {"name": proxy})
    print("switched to:", proxy)

def main():
    group = request("/proxies/" + quote(GROUP))
    current = group.get("now")
    candidates = group.get("all", [])
    failures = 0

    while True:
        delay = test_proxy(current)
        if delay is None:
            failures += 1
            print("unavailable:", current, "failure:", failures)
        else:
            failures = 0
            print("healthy:", current, "delay:", delay, "ms")

        if failures >= FAIL_LIMIT:
            for candidate in candidates:
                if candidate == current:
                    continue
                candidate_delay = test_proxy(candidate)
                if candidate_delay is not None:
                    switch_to(candidate)
                    current = candidate
                    failures = 0
                    break

        time.sleep(60)

if __name__ == "__main__":
    main()

脚本中的 all 字段由策略组类型和 Clash 内核版本决定,某些版本也可能返回 proxies 或其他结构。首次运行时,建议先打印完整的 API 返回值,确认策略组名称、当前节点字段和候选节点字段,再进行自动化改造。不要直接假设所有客户端的 JSON 结构完全相同,尤其是 Clash for Windows、Clash Verge Rev 与 Mihomo 在功能支持上可能存在差异。

更稳妥的切换策略:先测试所有候选节点并记录延迟,再从可用节点中选择延迟最低者;如果主要目标是高可用而不是低延迟,则按主节点、备用节点、灾备节点的固定顺序选择,避免网络轻微波动导致频繁换路。

生产环境中的稳定性、日志与排错

自动切换系统最常见的问题不是 API 不会调用,而是判断条件过于简单。一个节点的延迟升高,并不一定代表它已经不可用;测试地址被临时阻断,也不一定说明整条线路故障。因此,健康检查应区分“可用性”和“性能”两个维度。前者决定是否允许继续使用,后者只用于在多个可用节点之间择优。

  • 设置失败阈值:建议连续失败 2 至 3 次再切换,避免单次丢包触发故障转移。
  • 设置恢复阈值:备用节点连续成功若干次后,再考虑切回主节点,避免主备之间来回震荡。
  • 增加冷却时间:切换后至少等待 60 秒再进行下一次主动切换,给 TCP、TLS 和 DNS 连接留下恢复时间。
  • 保留当前节点:如果当前节点仍然可用,即使另一个节点延迟更低,也不必立即切换,可以设置 50 至 100 毫秒的延迟差阈值。
  • 记录切换原因:日志中写入时间、当前节点、测试结果、目标节点和失败次数,便于区分线路故障与脚本误判。

如果 API 返回 401 Unauthorized,通常是 secret 不正确或 Bearer 格式遗漏;返回 404,可能是接口路径、策略组名称或内核版本不匹配;返回连接拒绝,则应检查 Clash 是否启动、监听端口是否正确,以及脚本是否运行在另一台设备上。中文策略组名称导致的编码问题也很常见,必须使用 URL 编码后的名称,不能直接把空格或中文拼接进路径。

如果请求可以成功,但测试结果始终超时,应先在运行 Clash 的同一台机器上执行 curl,确认测试 URL 可以访问。某些节点只支持 TCP 或只支持 UDP,某些网络还会拦截特定探测域名;此时可以准备两个或三个不同的测试地址,并要求至少一个地址成功后才判定节点可用。测试地址最好与实际业务类型接近,例如主要访问网页就使用 HTTPS 轻量请求,主要运行游戏或实时通信则不能只依赖网页延迟。

使用环境变量启动脚本
# Linux/macOS
export CLASH_API="http://127.0.0.1:9090"
export CLASH_SECRET="replace-with-your-secret"
python3 auto_switch.py

# Windows PowerShell
$env:CLASH_API = "http://127.0.0.1:9090"
$env:CLASH_SECRET = "replace-with-your-secret"
python .\auto_switch.py

对于普通桌面用户,Clash 内置的 fallbackurl-test 策略组通常已经足够,不必额外运行脚本。API 自动切换更适合需要自定义规则的场景,例如按业务选择测试地址、在多个策略组之间联动、发送故障通知,或在远程服务器上执行无人值守运维。部署前先用手动 curl 验证接口,再逐步加入日志、失败阈值和切换冷却机制,能够避免脚本错误影响全部网络流量。

当你需要将节点健康检查、故障转移和运行状态监控整合到一套稳定流程中时,Clash 的 external-controller 提供了足够灵活的基础。只要控制好 API 暴露范围,选择合理的探测策略,并对自动切换设置明确的边界,就能在不频繁手动操作的情况下,让代理连接更具连续性和可维护性。

立即开始

用 Clash 掌控你的流量

支持 Windows、macOS、Linux、Android 与 iOS,灵活规则,开箱即用。

免费下载 查看设置指南 →