Clash API自动切换代理节点:Mihomo外部控制器进阶配置指南

为什么用 Clash API 自动切换代理节点? 详解

平时在 Clash Verge、Mihomo Party 或其他 Mihomo 客户端里切节点,打开代理页面、找到策略组再点选节点,手动操作并不复杂。但当你需要定时切换出口、在脚本里根据任务选择节点,或在多台设备上执行相同操作时,反复点界面就不够灵活了。此时可以使用 Mihomo 的外部控制器 API:客户端界面与 API 都在控制同一个 Mihomo 内核,脚本可以查询代理组、读取当前选择,再向指定策略组提交切换请求。

需要先区分两种「自动切换」。一种是 Mihomo 配置中的 url-test、fallback 或 load-balance 等策略组,由内核按照测速结果或可用状态选节点;另一种是通过 API 按照你自己的条件选择节点,例如任务开始时固定到某节点、任务结束后恢复原选择,或者由外部监控程序在故障时切换。API 本身不会替你决定哪条线路最好,它提供的是可编排的控制入口;选择逻辑、切换频率和失败后的处理,仍需由配置与脚本共同定义。

本文聚焦第二种用法,演示如何安全启用 external-controller、设置访问密钥、查询代理组,以及用 curl 和 Python 切换节点。不同客户端的设置菜单和配置文件管理方式可能略有区别,但只要运行的是支持该 API 的 Mihomo 内核,核心请求方式基本一致。

先确认控制对象:外部控制器连接的是 Mihomo 内核,不是某个客户端的网页界面。若你在配置里改了控制器端口或密钥,需确认客户端实际加载了这份配置,并在内核重载后再测试。没有启用 Mihomo 的旧版 Clash 核心,不能据此假设支持相同接口。

启用 external-controller:先限制监听范围 全面解析

外部控制器由配置项 external-controller 指定监听地址与端口,常见本机配置是 127.0.0.1:9090。这个地址只允许本机访问,适合个人电脑上的自动化脚本。建议同时设置非空的 secret:控制器 API 能查询连接、调整策略组,甚至影响整台设备的代理出口,因此不应把无认证的控制接口暴露到局域网或公网。

external-controller: 127.0.0.1:9090
secret: "请替换为足够长且随机的密钥"

如果客户端通过图形界面管理配置,不要急着直接改订阅原文件。先查看客户端的配置编辑、覆写或内核设置入口,再确认最终生效配置中包含控制器设置。部分客户端会在更新订阅时重新生成配置,直接编辑临时文件可能导致改动被覆盖。配置保存后,按客户端提供的方式重载配置或重启内核;只关掉设置窗口并不一定会使新端口立即生效。

安全上,127.0.0.1 与 0.0.0.0 的差别非常重要:后者通常意味着监听所有网络接口。除非你明确需要从另一台设备访问,并已通过防火墙、可信网络和强密钥限制来源,否则不要把控制器改成对外监听。不要把密钥放进公开仓库、截图、共享日志或命令历史;如果需要在多台设备运行脚本,可使用环境变量或本机权限受控的密钥文件,而不是把密钥硬编码在代码中。

不要将控制器端口当作代理端口:external-controller 是管理 API 的入口,不是 HTTP、SOCKS 或混合代理端口。把浏览器代理指向 9090 通常不会得到正常代理服务;反过来,也不要因为本机代理端口可连接,就推断控制器已经正确启用。

先查询代理组,再确认可选节点 一次讲透

切换前先通过 GET /proxies 查看 Mihomo 当前识别到的代理与策略组。接口返回 JSON,顶层的 proxies 对象包含名称、类型、当前选择以及可选项等信息。常见代理组名称包括 节点选择、Proxy 或机场自定义名称,但名称取决于配置,不能照搬示例。组的 type 也值得检查:Selector 一般允许手动指定选项;URLTest 等自动测速组通常由内核按自身逻辑选择,手动切换它们的结果不应与切换手动选择组混为一谈。

curl --fail --silent --show-error \
  -H "Authorization: Bearer 请替换为你的密钥" \
  http://127.0.0.1:9090/proxies

如果只想查看某个组,可请求 GET /proxies/{组名}。组名中若有空格、斜线或其他特殊字符,应进行 URL 编码;在终端中手动拼接路径时尤其容易把请求发到错误地址。响应里重点看 type、now 和 all:now 是目前选择,all 列出这个组可选的项目。某些项目可能是另一个策略组,而不是具体服务器节点,因此切换前要根据返回内容确认你选的是想要的层级。

  • 请求返回 401:通常表示没有提供有效密钥,检查请求头中的 Authorization: Bearer ... 是否与当前 Mihomo 配置一致。
  • 请求返回 404:先核对端口、请求路径和组名是否准确;组名大小写、空格以及 URL 编码都可能影响定位。
  • 连接被拒绝或超时:确认内核正在运行、监听端口与配置一致,并检查客户端是否把控制器绑定到了另一个地址。
  • 能查到组但看不到预期节点:检查该节点是否实际载入,以及它是否属于你查询的那一层代理组。

建议先在输出中找到准确的组名和目标节点名,再执行切换。不要假设订阅刷新后名称永远不变:服务商可能调整节点命名,配置覆写也可能重命名策略组。自动化脚本若依赖固定名称,应在每次调用时验证目标是否仍在 all 列表中,而不是收到异常后继续发送盲目请求。

用 curl 切换节点并验证结果 深入理解

确认策略组为可手动选择的类型,并且目标节点出现在其可选列表中后,可使用 PUT /proxies/{组名} 更新选择。请求体是 JSON,键名为 name,值为要切换到的选项名称。下面的例子使用占位组名和节点名;请替换为 /proxies 响应里的真实值。路径中的组名应按 URL 规则编码,避免特殊字符造成接口找不到资源。

curl --fail --silent --show-error \
  -X PUT \
  -H "Authorization: Bearer 请替换为你的密钥" \
  -H "Content-Type: application/json" \
  -d '{"name":"目标节点名称"}' \
  "http://127.0.0.1:9090/proxies/策略组名称"

切换请求成功后,不要只凭 HTTP 请求没有报错就判断任务完成。再次查询对应代理组,确认 now 已变为目标节点;随后还可以发起一次实际网络请求,并在客户端连接列表或日志里核对请求所用的出站。API 返回成功只能说明控制器接受了选择请求,并不保证目标节点当前可用、远端服务能访问,或应用流量确实经过了预期策略组。

如果策略组名称包含空格,构造接口路径时需要编码,例如空格可表示为 %20。不同命令行环境对引号与百分号的处理可能不同;用变量拼接 URL 时,也应避免未编码的用户输入直接进入路径。对于定时任务,最好将组名和节点名作为明确配置项保存,并在运行前检查它们仍存在,减少订阅变更后误选或请求失败的情况。

动手操作:用 Python 查询并切换策略组 详解

下面的 Python 示例使用标准库 urllib,不需要额外安装 HTTP 客户端。它先查询指定策略组,验证目标节点属于可选项,再发送切换请求,最后重新查询当前选择。将密钥放在环境变量 MIHOMO_SECRET 中,避免写入脚本文件;组名和节点名则按本机 API 返回值修改。

import json
import os
from urllib.error import HTTPError, URLError
from urllib.parse import quote
from urllib.request import Request, urlopen

BASE = "http://127.0.0.1:9090"
GROUP = "节点选择"
TARGET = "目标节点名称"
SECRET = os.environ["MIHOMO_SECRET"]

def api(method, path, body=None):
    data = None if body is None else json.dumps(body).encode("utf-8")
    request = Request(
        BASE + path,
        data=data,
        method=method,
        headers={
            "Authorization": f"Bearer {SECRET}",
            "Content-Type": "application/json",
        },
    )
    with urlopen(request, timeout=5) as response:
        content = response.read()
        return json.loads(content) if content else None

group_path = "/proxies/" + quote(GROUP, safe="")
try:
    current = api("GET", group_path)
    options = current.get("all", [])

    if TARGET not in options:
        raise ValueError(f"目标节点不在策略组选项中: {TARGET}")

    if current.get("now") == TARGET:
        print(f"当前已使用目标节点: {TARGET}")
    else:
        api("PUT", group_path, {"name": TARGET})
        updated = api("GET", group_path)
        if updated.get("now") != TARGET:
            raise RuntimeError("切换请求已提交,但当前选择未变更")
        print(f"已切换到: {TARGET}")

except HTTPError as exc:
    print(f"API 返回 HTTP 错误: {exc.code}")
except (URLError, TimeoutError) as exc:
    print(f"无法连接 Mihomo 控制器: {exc}")
except (KeyError, ValueError, RuntimeError) as exc:
    print(f"自动切换未完成: {exc}")

在 macOS 或 Linux 的终端中,可以在启动脚本前设置环境变量,例如 export MIHOMO_SECRET='你的密钥';Windows PowerShell 可使用 $env:MIHOMO_SECRET='你的密钥'。不要把真实密钥粘贴到公开终端录屏或共享 CI 日志里。示例设置了五秒超时,目的是避免控制器无响应时任务永久挂起;生产脚本还可加入日志记录、有限次数重试和告警,但不建议无间隔地连续重试。

若脚本由定时器或自动化平台运行,需确认它与 Mihomo 在同一网络命名空间内,并且运行时能够访问 127.0.0.1:9090。容器中的 127.0.0.1 指向容器自身,而不一定是宿主机;远程服务器上的计划任务也不会自动连接你个人电脑的本地控制器。对于这类部署,应先设计受限的网络访问方式,再配置控制器地址,不能简单地把监听地址改成公网地址。

自动化设计:别让测速与脚本互相抢控制权 全面解析

API 自动切换最容易出问题的地方,往往不是请求格式,而是多个机制同时控制同一个策略组。例如,脚本刚把 Selector 切到指定节点,另一个任务又在几秒后切走;或者用户正在执行长时间下载时,定时任务按固定间隔轮换节点,导致连接中断。要让自动化可预测,先明确每个策略组的控制者:哪些由用户手动选择,哪些由 url-test 自动选择,哪些只允许特定脚本修改。

  • 按任务切换:开始任务前保存原来的 now,切到目标节点;任务结束后可选择恢复原节点。恢复前要再次确认当前值,避免覆盖用户在任务期间主动做出的更改。
  • 按健康状态切换:先用独立的连通性检查判断故障,再切换到备用节点,并设置冷却时间。不要把一次短暂超时直接当成节点永久失效。
  • 按时段切换:使用明确的时间表并记录每次变更。应考虑系统睡眠、时区和任务重叠,避免设备唤醒后多个过期任务一起执行。
  • 按应用分流:优先在规则与策略组层面规划应用走向;只有确实需要外部流程主动改变当前选择时,才用 API 进行全局节点切换。

节点切换不等于已有连接会无缝迁移。多数正在传输的 TCP 会话仍可能因为出口变化而断开,长连接、下载任务和远程登录尤其明显。对这类业务,更稳妥的做法是只在任务开始前切换,或把不同用途放在独立策略组中,让普通流量不受自动任务影响。切换后应留出连接重建时间,并通过实际业务请求验证结果,而不是用连续快速切换来「测试稳定性」。

建议为脚本加上幂等判断:如果当前节点已经是目标,就不再发送写入请求;如果目标不存在,就明确报错并停止;如果 API 请求超时,不要立即假定切换失败,因为内核可能已经处理了请求。此时先重新查询当前状态,再决定是否重试。还可以为每次操作记录时间、策略组、原选择、目标选择和结果,但日志里不要记录密钥,也不要记录可能泄露订阅凭据的完整配置内容。

常见问题:认证失败、端口不可达与切换无效 一次讲透

为什么返回 401 或提示认证失败?(详解)

确认控制器配置中的 secret 与脚本使用的密钥完全一致,检查是否多了空格、换行或引号字符,并确认请求头格式是 Authorization: Bearer 密钥。若刚改过配置,先确认 Mihomo 已加载新设置。不要通过移除认证来绕过问题;本机控制接口仍应设置密钥。

为什么 127.0.0.1:9090 连接失败?(要点)

先检查 Mihomo 内核是否运行,以及控制器配置的端口是否确实为 9090。客户端有时会管理独立的内核配置,用户编辑的另一份 YAML 可能并未生效。若脚本运行在容器、虚拟机或远程主机中,127.0.0.1 指向脚本所在环境,而不是桌面客户端所在设备;应先厘清网络边界,再决定如何安全连通。

请求成功了,为什么节点看起来没有切换?(说明)

重新查询组的 now 和 all,确认请求地址中的组名正确、目标名称与可选项完全一致,并检查该组是不是手动选择类型。若它属于自动测速组,内核可能会按组的机制更新结果;若有另一段脚本同时运行,也可能立即覆盖你的选择。切换后还要检查客户端连接详情,确认实际请求命中了预期的出站。

API 可访问,但代理请求仍然失败,怎么判断?(实践)

控制器 API 与代理转发是两条不同路径。API 能响应只代表本机管理端口可用,不代表节点可用、规则命中正确或应用已经走代理。查看 Mihomo 的连接记录,核对目标域名、规则命中项和出站名称;再用同一代理组发起简单请求。如果 API 已成功切换而业务仍失败,应继续排查节点状态、策略组层级、规则和应用代理设置,而不是反复修改控制器端口。

与手动在不同客户端界面里逐个寻找策略组相比,直接调用 API 更适合需要查询状态、复核结果和纳入任务流程的场景;但它也要求你认真管理密钥、监听范围和并发控制,单纯依赖定时轮换或复制一条命令并不能解决节点质量问题。Clash 将 Mihomo、Clash 客户端配置与常见排障步骤放在同一套中文资料中,便于按客户端和实际故障逐项核对;如果你希望先选好合适的客户端,再按本文搭建自动切换流程,可以前往下载并从本机受限的控制器配置开始测试。