Clash API 自動切替を始める前の考え方
ノードの障害や急な遅延が起きるたびに、Clash の画面を開いて別のプロキシを選ぶ運用は、短時間なら対応できても、長時間の作業やサーバー上のバッチ処理には向きません。特に、動画会議、パッケージの取得、リモート開発、API クライアントの実行中に接続が止まると、手動操作が間に合わず処理全体が失敗することがあります。そこで役立つのが、Mihomo API からプロキシグループの状態を読み取り、ヘルスチェックの結果に応じて利用ノードを切り替える構成です。
ここでいう自動切替は、単に「応答が速いノードを常に選ぶ」仕組みではありません。対象グループに登録されたノードへ定期的にリクエストを送り、応答時間、HTTP ステータス、タイムアウトの有無を確認し、その時点で利用可能な候補を選びます。url-test や fallback を YAML 側で使う方法もありますが、API スクリプトを組み合わせると、ログ保存、通知、時間帯ごとの選択、特定ノードの除外などを細かく制御できます。
前提:以下は自分が管理する Clash または Mihomo のローカルコントローラを操作する例です。公開サーバーの API を探索したり、他人の設定を変更したりする用途では使用しないでください。API の外部公開は避け、管理用ポートを 127.0.0.1 に限定する構成を推奨します。
クライアントによって画面上の名称は異なります。Clash Verge Rev、Mihomo Party、Clash for Android などでは「外部コントローラ」「External Controller」「Controller」と表示されることがあります。重要なのは GUI の製品名ではなく、実際に稼働しているコアが Mihomo API を提供しているか、そして API ポートと Secret が何に設定されているかです。
Mihomo API の接続確認と認証
Mihomo の外部コントローラは、通常 127.0.0.1:9090 のようなアドレスで待ち受けます。設定例は次のようになります。すでにクライアントが生成した設定を利用している場合は、同じ項目を重複して追加せず、既存値を確認してください。
external-controller: 127.0.0.1:9090
secret: "change-this-to-a-long-random-value"
/version は API の疎通確認に使いやすいエンドポイントです。Secret を設定している場合、HTTP ヘッダーの Authorization に Bearer と Secret を渡します。
curl -s \
-H "Authorization: Bearer ${CLASH_SECRET}" \
http://127.0.0.1:9090/version
レスポンスが JSON で返れば、ポート、アドレス、Secret の組み合わせは概ね正しい状態です。接続できないときは、まず Clash のログで API の待ち受けアドレスを確認し、次に別プロセスが同じポートを使っていないか調べます。127.0.0.1 と 0.0.0.0 を取り違えるケースもあります。後者は LAN 上の他端末から到達できる可能性があるため、必要性がなければ避けてください。
プロキシグループの一覧は /proxies から取得できます。グループ名に日本語や空白が含まれる場合、URL に直接埋め込まず、クライアント側で URL エンコードしてください。API のレスポンスにはグループの種類、現在選択されているプロキシ、利用可能な候補が含まれます。
curl -s \
-H "Authorization: Bearer ${CLASH_SECRET}" \
http://127.0.0.1:9090/proxies
Secret の保護:Secret をシェルスクリプトへ直接書き込んだり、Git リポジトリへコミットしたりしないでください。環境変数、OS の資格情報ストア、権限を制限した設定ファイルなどを使い、ログには Authorization ヘッダーを出力しないようにします。
プロキシグループを API から切り替える
プロキシグループの変更には、一般的に PUT /proxies/{group} を使用します。たとえばグループ名が Proxy で、切り替え先が Tokyo-01 の場合は次のように送信します。
curl -X PUT \
-H "Authorization: Bearer ${CLASH_SECRET}" \
-H "Content-Type: application/json" \
-d '{"name":"Tokyo-01"}' \
"http://127.0.0.1:9090/proxies/Proxy"
ただし、グループ名とノード名は購読設定によって異なります。「自動選択」「Proxy」「GLOBAL」など、実際の /proxies レスポンスに存在する名前を使ってください。存在しない名前を指定すると、API の形式が正しくても切替は失敗します。また、url-test グループを手動で変更すると、そのグループ本来の自動選択動作を一時的に上書きする場合があるため、運用方針を先に決めておくことが大切です。
安全な切替処理は、次の順序にすると原因を追いやすくなります。
- 対象グループを確認:
/proxiesからグループ名、現在のノード、候補一覧を取得します。 - 候補を絞り込む:拒否したい地域名やテスト用ノード、直近で失敗したノードを除外します。
- ヘルスチェックを実行:候補ごとの遅延とステータスを記録し、タイムアウトした候補は一時的に避けます。
- 切替を実行:最適な候補へ PUT を送り、直後に GET して実際の選択状態を確認します。
- 結果を記録:時刻、グループ名、旧ノード、新ノード、測定値だけを保存します。Secret や購読 URL は記録しません。
切替直後にすべての通信が改善するとは限りません。すでに確立した TCP 接続や WebSocket は古いノードを保持したままになることがあり、新しい接続から切替結果が反映されます。長時間接続を使うアプリでは、Clash の接続一覧で対象ホストを確認し、必要に応じて該当接続だけを閉じて再接続させます。
Python と Shell で作る自動化スクリプト
Python では標準ライブラリだけでも API 呼び出しを実装できます。次の例は、グループ情報を取得し、候補の中から名前に特定の文字列を含むノードを選び、現在のノードと異なる場合だけ切り替える最小構成です。実運用では、実際の API レスポンスを確認してグループ名と候補の構造を調整してください。
import json
import os
import urllib.request
import urllib.parse
BASE = os.environ.get("CLASH_API", "http://127.0.0.1:9090")
SECRET = os.environ["CLASH_SECRET"]
GROUP = os.environ.get("CLASH_GROUP", "Proxy")
def request(path, method="GET", payload=None):
data = None
headers = {"Authorization": f"Bearer {SECRET}"}
if payload is not None:
data = json.dumps(payload).encode()
headers["Content-Type"] = "application/json"
req = urllib.request.Request(
BASE + path, data=data, headers=headers, method=method
)
with urllib.request.urlopen(req, timeout=8) as response:
return json.loads(response.read().decode())
encoded_group = urllib.parse.quote(GROUP, safe="")
data = request("/proxies")
group = data["proxies"][GROUP]
current = group.get("now", "")
candidates = [
name for name in group.get("all", [])
if "Tokyo" in name and name != current
]
if candidates:
target = candidates[0]
request(f"/proxies/{encoded_group}", "PUT", {"name": target})
print(f"switched: {current} -> {target}")
else:
print("no candidate found")
この例では名前順の最初の候補を選んでいますが、実際の自動切替では候補の応答時間を比較する方が適切です。Mihomo の遅延測定 API を利用できる構成なら、候補ごとに測定を行い、一定時間内に成功したものだけを比較します。測定回数を増やしすぎると、ノード側や回線側に不要な負荷をかけるため、通常は 30 秒から数分に一度、失敗が続いたときだけ追加チェックする設計が現実的です。
Shell は、単純な監視や cron、タスクスケジューラとの組み合わせに向いています。JSON の解析に jq を使える環境なら、現在の選択状態を確認してから API を呼び出せます。
#!/usr/bin/env sh
set -eu
: "${CLASH_SECRET:?CLASH_SECRET is required}"
API="${CLASH_API:-http://127.0.0.1:9090}"
GROUP="${CLASH_GROUP:-Proxy}"
TARGET="${CLASH_TARGET:-Tokyo-01}"
current="$(
curl -fsS \
-H "Authorization: Bearer ${CLASH_SECRET}" \
"${API}/proxies" |
jq -r --arg group "$GROUP" '.proxies[$group].now'
)"
if [ "$current" != "$TARGET" ]; then
curl -fsS -X PUT \
-H "Authorization: Bearer ${CLASH_SECRET}" \
-H "Content-Type: application/json" \
-d "{\"name\":\"${TARGET}\"}" \
"${API}/proxies/$(printf '%s' "$GROUP" | jq -sRr @uri)"
printf '%s: %s -> %s\n' "$(date -Iseconds)" "$current" "$TARGET"
fi
cron で定期実行する場合は、環境変数が読み込まれないことに注意してください。Secret を crontab のコマンド行へ書くのではなく、所有者と権限を制限した環境ファイルを読み込む方法が安全です。ログローテーションも設定し、接続先のドメインやノード名だけを残して、認証情報、購読リンク、リクエスト本文を保存しないようにします。
切替の安定化:一度のタイムアウトだけで即座にノードを変更すると、短い瞬断のたびに切替が発生します。「3 回中 2 回失敗したら切替」「切替後は 60 秒間固定」「同じノードへ戻すまで最低 5 分待つ」といったヒステリシスを入れると、ノードが頻繁に行き来するフラッピングを抑えられます。
失敗時の確認と長期運用のポイント
スクリプトが動かない場合は、最初に API の疎通、次に認証、最後にグループ名とノード名を確認します。ブラウザで管理画面を開けることだけでは、スクリプトが正しいポートへ接続できる証明になりません。GUI が別の Mihomo コアを起動していたり、複数の Clash クライアントが同時に稼働していたりすると、見ている設定と操作対象がずれることがあります。
- 接続拒否:API ポートが無効、アドレスが異なる、または対象コアが起動していない可能性があります。
- 401 Unauthorized:Secret が一致していない、Bearer の記述が欠けている、環境変数に古い値が残っている可能性があります。
- 404 Not Found:API パス、グループ名、URL エンコードを確認します。画面上の表示名と YAML 内の実名が異なる場合もあります。
- 切替後も遅い:既存接続、DNS キャッシュ、対象ルールの別グループ指定が残っていないか、Clash の接続画面で確認します。
自動化を共有 PC や開発チームで使う場合は、誰が切替を許可されているかも決めておきます。ローカル API であっても、悪意のあるローカルプロセスから操作される可能性はゼロではありません。API ポートを LAN に公開しない、Secret を十分長くする、OS のファイアウォールで許可範囲を絞る、不要になったスクリプトを削除する、といった基本対策を先に実施してください。
市販の簡易プロキシ切替ツールは画面操作だけで始められる一方、API の認証を平文で保存したり、失敗判定の条件を変更できなかったり、Mihomo のグループ構成に対応しきれなかったりすることがあります。反対に、Clash 公式サイト ではクライアントの入手先と設定項目を確認しながら、Mihomo API、ログ確認、Secret 管理、Python/Shell の自動化を段階的に組み立てられます。まずは手動で /version と /proxies を確認し、環境に合うスクリプトへ発展させたい方は、ダウンロードするところから始めると無理なく試せます。