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

为什么用 Clash API 全全自动切换代理节点? 要点速览

本篇正文聚焦第二种用法,演示如何安全启用 external-controller、设置访问到到到密钥、查询代理组,以及用 curl 和 Python 切换节点。

平时在 Clash Verge、Mihomo Party 或其他 Mihomo 客户端里切节点,启用代理页面、找到策略组再点选节点,人工操作并不复杂。但当你要定时切换出口、在脚本里根据任务选定节点,或在多台设备上执行相同操作时,反复点UI UI UI 界面就不够可调了。

此时得以采用 Mihomo 的外部控制器 API:客户端UI UI UI 界面与 API 都在控制同一个 Mihomo 内核,脚本得以查询代理组、读取当前选定,再向指定策略组提交切换请求。

关键点是:要先区分两种「全全全自动切换」。一种是 Mihomo 配置中的 url-test、fallback 或 load-balance 等策略组,由内核按照测速结果或可用状态选节点;

另一种是通过 API 按照你自行的条件选定节点,举个小例子任务开始时固定到某节点、任务结束后恢复原选定,或者由外部监控程序在故障时切换。API 本身不会替你决定哪条线路最好,它提供的是可编排的控制入口;选定逻辑、切换频率和无法完成后的处理,仍需由配置与脚本共同定义。

不同客户端的设置菜单和配置文件管理方式或许略有不同之处,但只要持续持续持续运行的是适配该 API 的 Mihomo 内核,核心请求方式基本一致。

先确认控制对象:外部控制器连接的是 Mihomo 内核,不是某个客户端的网页UI UI UI 界面。

若你在配置里改了控制器端口或密钥,需确认客户端实际加载了这份配置,并在内核重载后再测试。没有启用 Mihomo 的旧版 Clash 核心,不能据此假设适配相同接口。

启用 external-controller:先限制监听范围 关键信息

安全上,127.0.0.1 与 0.0.0.0 的差别相当核心:后者多数现状下意味着监听各个网络接口。除非你明确要从另一台设备访问到到到,并已通过防火墙、可信网络和强密钥限制来源,否则不要把控制器改成对外监听。

这里要多说一句:外部控制器由配置项 external-controller 指定监听地址与端口,普遍本机配置是 127.0.0.1:9090。这个地址只允许本机访问到到到,适合个人电脑上的全全全自动化脚本。

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

不妨同时设置非空的 secret:控制器 API 能查询连接、调整策略组,甚至影响整台设备的代理出口,于是不应把无身份校验的控制接口暴露到局域网或公网。

如果客户端通过图形UI UI UI 界面管理配置,不要急着径直改订阅原文件。先查看客户端的配置编辑、覆写或内核设置入口,再确认最终生效配置中包含控制器设置。

换个说法:部分客户端会在升级订阅时重新生成配置,径直编辑临时文件或许导致改动被覆盖。配置保存后,按客户端提供的方式重载配置或重启内核;只关掉设置窗口并不一定会使新端口立即生效。

不要把密钥放进公开仓库、截图、共享日志或命令历史;如果要在多台设备持续持续持续运行脚本,可采用环境变量或本机权限受控的密钥文件,而不是把密钥硬编码在代码中。

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

先查询代理组,再确认可选节点 关键点梳理

不妨先在输出中找到准确的组名和目标节点名,再执行切换。不要假设订阅刷新后名称永远不变:服务商或许调整节点命名,配置覆写也或许重命名策略组。

切换前先通过 GET /proxies 查看 Mihomo 当前识别到的代理与策略组。接口返回 JSON,顶层的 proxies 对象包含名称、类型、当前选定以及可设置项等信息。普遍代理组名称涵盖了 节点选择、Proxy 或机场自定义名称,但名称取决于配置,不能照搬示例。

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

落到实处:组的 type 也值得排查:Selector 常规允许人工指定设置项;URLTest 等全全全自动测速组多数现状下由内核按自身逻辑选定,人工切换这几个的结果不应与切换人工选定组混为一谈。

如果只想查看某个组,可请求 GET /proxies/{组名}。组名中若有空格、斜线或其他特殊字符,应进行 URL 编码;在终端中人工拼接路径时尤其容易把请求发到疏漏地址。

响应里关键点看 type、now 和 all:now 是目前选定,all 列出这个组可选的项目。某些项目或许是另一个策略组,而不是具体服务器节点,于是切换前要根据返回正文确认你选的是想要的层级。

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

全全自动化脚本若依赖固定名称,应在每次调用时验证目标是否仍在 all 列表中,而不是收到异常后继续发送盲目请求。

用 curl 切换节点并验证结果 完备说明

如果策略组名称包含空格,构造接口路径时要编码,举个小例子空格可表示为 %20。不同命令行环境对引号与百分号的处理或许不同;用变量拼接 URL 时,也应避免未编码的用户输入径直进入路径。

确认策略组为可人工选定的类型,并且目标节点出现在其可选列表中后,可采用 PUT /proxies/{组名} 升级选定。请求体是 JSON,键名为 name,值为要切换到的设置项名称。

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

关键点是:下面的例子采用占位组名和节点名;请替换为 /proxies 响应里的真实值。路径中的组名应按 URL 规则编码,避免特殊字符造成接口找不到资源。

切换请求成功后,不要只凭 HTTP 请求没有给出疏漏就判断任务完成。再次查询对应代理组,确认 now 已变为目标节点;随后还得以发起一次实际网络请求,并在客户端连接列表或日志里核对请求所用的出站。

API 返回成功只能说明控制器接受了选定请求,并不保证目标节点当前可用、远端服务能访问到到,或应用流量确实经过了预期策略组。

这里要多说一句:对于定时任务,最好将组名和节点名作为明确配置项保存,并在持续持续运行前排查这几个仍存在,减少订阅变更后误选或请求无法完成的现状。

动手操作:用 Python 查询并切换策略组 要点速览

若脚本由定时器或全全自动化平台持续持续运行,需确认它与 Mihomo 在同一网络命名空间内,并且持续持续运行时能够访问到到 127.0.0.1:9090。容器中的 127.0.0.1 指向容器自身,而不一定是宿主机;

下面的 Python 示例采用标准库 urllib,不要额外安装 HTTP 客户端。它先查询指定策略组,验证目标节点属于可设置项,再发送切换请求,最后重新查询当前选定。

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}")

将密钥放在环境变量 MIHOMO_SECRET 中,避免写入脚本篇正文件;组名和节点名则按本机 API 返回值修改。

换个说法:在 macOS 或 Linux 的终端中,得以在开启脚本前设置环境变量,举个小例子 export MIHOMO_SECRET='你的密钥';Windows PowerShell 可采用 $env:MIHOMO_SECRET='你的密钥'。

不要把真实密钥粘贴到公开终端录屏或共享 CI 日志里。示例设置了五秒超时,目的是避免控制器无响应时任务永久挂起;生产脚本还可加入日志记录、有限次数重试和告警,但不不妨无间隔地连续重试。

远程服务器上的计划任务也不会全全自动连接你个人电脑的本地控制器。对于这类铺开,应先设计受限的网络访问到到方式,再配置控制器地址,不能轻便地把监听地址改成公网地址。

全自动化设计:别让测速与脚本互相抢控制权 关键信息

不妨为脚本加上幂等判断:如果当前节点已经是目标,就不再发送写入请求;如果目标不存在,就明确给出疏漏并停止;如果 API 请求超时,不要立即假定切换无法完成,因为内核或许已经处理了请求。

落到实处:API 全全自动切换最容易出困扰的地方,经常不是请求格式,而是多个机制同时控制同一个策略组。举个小例子,脚本刚把 Selector 切到指定节点,另一个任务又在几秒钟钟后切走;

或者用户正在执行长时间下载时,定时任务按固定间隔轮换节点,导致连接中断。要让全全自动化可预测,先明确每个策略组的控制者:哪些由用户人工选定,哪些由 url-test 全全自动选定,哪些只允许特定脚本修改。

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

别忽略了:节点切换不等于已有连接会无缝迁移。多数正在传输的 TCP 会话仍或许因为出口变化而断开,长连接、下载任务和远程登录尤其清楚可见。

对这类业务,更稳妥的做法是只在任务开始前切换,或把不同用途放在独立策略组中,让普通流量不受全全自动任务影响。切换后应留出连接重建时间,并通过实际业务请求验证结果,而不是用连续敏捷切换来「测试平稳性」。

此时先重新查询当前状态,再决定是否重试。还得以为每次操作记录时间、策略组、原选定、目标选定和结果,但日志里不要记录密钥,也不要记录或许泄露订阅凭据的完备配置正文。

普遍困扰:身份校验无法完成、端口不可达与切换无效 关键点梳理

为什么返回 401 或提示身份校验无法完成?(速览)

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

为什么 127.0.0.1:9090 连接无法完成?(要点)

先排查 Mihomo 内核是否持续持续运行,以及控制器配置的端口是否确实为 9090。客户端有时会管理独立的内核配置,用户编辑的另一份 YAML 或许并未生效。

关键点是:若脚本持续持续运行在容器、虚拟机或远程主机中,127.0.0.1 指向脚本所在环境,而不是桌面客户端所在设备;应先厘清网络边界,再决定如何安全连通。

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

重新查询组的 now 和 all,确认请求地址中的组名对路、目标名称与可设置项完全一致,并排查该组是不是人工选定类型。

若它属于全全自动测速组,内核或许会按组的机制升级结果;若有另一段脚本同时持续持续运行,也或许立即覆盖你的选定。切换后还要排查客户端连接详情,确认实际请求命中了预期的出站。

API 可访问到,但代理请求仍然无法完成,怎么判断?(说明)

与人工在不同客户端UI UI 界面里逐个寻找策略组相比,径直调用 API 更适合要查询状态、复核结果和纳入任务流程的采用使用场景;但它也要求你认真管理密钥、监听范围和并发控制,单纯依赖定时轮换或复制一条命令并不能处置节点质量困扰。

控制器 API 与代理转发是两条不同路径。API 能响应只代表本机管理端口可用,不代表节点可用、规则命中对路或应用已经走代理。查看 Mihomo 的连接记录,核对目标域名、规则命中项和出站名称;

这里要多说一句:再用同一代理组发起轻便请求。如果 API 已成功切换而业务仍无法完成,应继续排查节点状态、策略组层级、规则和应用代理设置,而不是反复修改控制器端口。

Clash 将 Mihomo、Clash 客户端配置与普遍排障环节放在同一套中文资料中,便于按客户端和实际故障逐项核对;如果你希望先选好合适的客户端,再按本篇正文搭建全全自动切换流程,得以前往下载并从本机受限的控制器配置开始测试。