Clash APIで自動切替を始める前に理解したい構成
Clash の画面でノードを手動選択する運用は、短時間の検証には便利ですが、外出先の Wi-Fi、混雑する夜間、長時間の API 通信など、接続状態が変化する環境ではすぐに限界が見えてきます。そこで利用したいのが external-controller と呼ばれる管理 API です。Mihomo や Clash Meta 系のコアでは、HTTP API を通じて現在の設定、プロキシグループ、接続情報、遅延測定結果などを取得できます。さらに API からグループの選択先を変更すれば、画面を開かずにノードを自動切替できます。
重要なのは、API が通信そのものを中継する機能ではなく、Clash コアへ「状態を教えてもらう」「設定を変更する」ための管理窓口だという点です。通常のプロキシポートである mixed-port や socks-port と、管理用の external-controller は役割が異なります。たとえばブラウザの通信は mixed-port に送りますが、スクリプトは external-controller の HTTP エンドポイントへリクエストを送ります。この二つを混同すると、API の URL にプロキシポートを指定して接続できない、または管理ポートを一般通信に使ってしまうといった設定ミスが起こります。
自動切替の判断材料は、単純な ping 値だけではありません。測定 URL への応答時間、直近の HTTP ステータス、接続失敗回数、ノードの地域や用途、現在の利用中グループなどを組み合わせて考える必要があります。遅延が最小でも、一定時間ごとに切断されるノードを選ぶと、動画や WebSocket、長時間の API セッションではかえって不安定になります。したがって本稿では「最速のノードを常に選ぶ」のではなく、測定・失敗判定・切替・クールダウンを分けた運用を前提にします。
mixed-port は端末アプリの通信入口、external-controller は Clash を操作する管理入口です。管理 API はローカルホストだけで待ち受けさせ、外部ネットワークから直接アクセスできない状態を基本にしてください。
external-controller と secret を安全に設定する
最初に設定ファイルで管理 API の待ち受け先と認証用シークレットを確認します。Mihomo 系の構成では、概念的には次のような項目を使います。実際のキー名や許可される形式は、利用しているクライアントとコアのバージョンに合わせて公式ドキュメントで確認してください。
external-controller: 127.0.0.1:9090
secret: "replace-with-a-long-random-secret"
127.0.0.1 を指定すると、同じ端末上のスクリプトからだけ接続できます。家庭内 LAN の別端末から API を操作したい場合は待ち受けアドレスを変更する方法もありますが、その場合はファイアウォール、アクセス元の制限、強い secret、管理ネットワークの分離を組み合わせてください。0.0.0.0:9090 のように全インターフェースへ無条件で公開する設定は、ルーターやホテル Wi-Fi のような信頼できないネットワークでは避けるべきです。
secret は短い文字列や購読 URL と同じ値を使わず、パスワード管理ツールなどで生成した長いランダム値にします。シェルの履歴、Git リポジトリ、共有スクリーンショット、監視ログへ secret が出ないように注意してください。API クライアントでは通常、Authorization: Bearer ヘッダーとして送信します。設定を変更した後はコアを再起動するか、利用中の GUI から設定を再読み込みし、管理 API が実際に待ち受けていることを確認します。
APIでグループ・遅延・現在状態を確認する
自動切替を作る前に、まず手動で API を呼び、どのようなデータが取得できるかを確認します。代表的な管理エンドポイントには、プロキシ一覧を取得する /proxies、特定のプロキシやグループの遅延を測定する URL、現在の設定情報を取得するエンドポイントなどがあります。クライアントによって API の細部が異なるため、まず /proxies の JSON を保存し、実際のグループ名とプロキシ名を確認するのが安全です。
curl -s \
-H "Authorization: Bearer $CLASH_SECRET" \
http://127.0.0.1:9090/proxies
返却された JSON では、一般に Selector、URLTest、Fallback などのグループと、その配下にあるノード名を区別して読み取ります。グループ名は購読者ごとに異なり、「Proxy」「PROXY」「🚀 节点选择」のように表記が変わることがあります。スクリプトへ画面表示名を固定的に書き込む前に、対象グループの名前を設定ファイルまたは API のレスポンスで確定してください。絵文字や日本語を含む名前では、シェルの文字コードや JSON のエスケープ処理も確認します。
自動切替に使う測定 URL は、実際の用途に近いものを選びます。小さな HTTPS 応答を返す URL はノードの応答性を見るのに向いていますが、動画サービスの再生品質や大容量ダウンロードの帯域を保証するものではありません。業務 API の応答を見るなら、そのサービスを過剰に叩かない低頻度のヘルスチェック URL を用意します。測定先が落ちているだけなのにノード障害と判定しないよう、可能なら複数の測定先を使い、DNS、TLS、HTTP ステータスを別々に記録します。
| 確認項目 | 見る内容 | 切替判断への使い方 |
|---|---|---|
| 応答時間 | 測定 URL の HTTP 応答までの時間 | 短期的な優先順位を決める |
| 成功率 | 一定回数の成功・失敗割合 | 一時的な高速値を過信しない |
| 連続失敗 | タイムアウトや 5xx の連続回数 | 切替を実行するトリガーにする |
| 切替時刻 | 最後にグループを変更した時刻 | 頻繁なフラッピングを防ぐ |
curl と Python でプロキシグループを切り替える
グループの現在選択されているノードを変更する場合は、管理 API へ対象グループ名と新しいプロキシ名を送信します。よくある方式は、グループ名を URL エンコードしたパスへ指定し、JSON ボディで選択先を渡す形です。次の例は考え方を示す最小構成であり、名前は手元の /proxies の結果へ置き換えてください。
curl -X PUT \
-H "Authorization: Bearer $CLASH_SECRET" \
-H "Content-Type: application/json" \
--data '{"name":"Node-A"}' \
"http://127.0.0.1:9090/proxies/PROXY"
ただし、候補名を固定しただけのスクリプトは、購読更新でノード名が変わると簡単に壊れます。実運用では API から対象グループを読み、利用可能な候補を抽出し、測定結果を比較してから PUT を実行します。ノード名に地域名や番号が含まれていても、文字列の部分一致だけで選ぶと「香港」を含むバックアップ名まで誤って選ぶことがあります。候補リストを明示する、あるいは設定側で自動切替用のグループを分けるなど、選定条件を読める形にしておくと保守しやすくなります。
Python を使う場合は、secret をソースコードへ直接書かず環境変数から読み込みます。HTTP タイムアウトを必ず設定し、API が応答しないときにスクリプト自体が無期限に固まらないようにします。また切替処理の前後で現在値をログへ残し、同じノードへの不要な再選択を避けます。ログには secret、購読 URL、個人を識別できる接続先を出さず、時刻、グループ、候補名、測定結果、実行結果だけを記録するのが無難です。
import os
import time
import requests
BASE = "http://127.0.0.1:9090"
GROUP = "PROXY"
SECRET = os.environ["CLASH_SECRET"]
HEADERS = {"Authorization": f"Bearer {SECRET}"}
def choose_proxy(candidates):
best_name = None
best_delay = None
for name in candidates:
try:
response = requests.get(
f"{BASE}/proxies/{name}/delay",
params={"url": "https://www.gstatic.com/generate_204", "timeout": 5000},
headers=HEADERS,
timeout=8,
)
delay = response.json().get("delay")
if isinstance(delay, int) and (best_delay is None or delay < best_delay):
best_name, best_delay = name, delay
except (requests.RequestException, ValueError):
continue
return best_name, best_delay
def switch_to(name):
response = requests.put(
f"{BASE}/proxies/{GROUP}",
json={"name": name},
headers=HEADERS,
timeout=8,
)
response.raise_for_status()
candidates = ["Node-A", "Node-B", "Node-C"]
name, delay = choose_proxy(candidates)
if name:
switch_to(name)
print(f"selected={name} delay={delay}")
else:
print("no healthy candidate")
頻繁な切替を防ぐヒステリシスとクールダウン
測定値が 80ms と 90ms の間で揺れるたびにノードを変更すると、TCP セッションやログイン状態が切断され、利用者の体感はむしろ悪化します。そこで「新しいノードが現在値より一定以上速い場合だけ切り替える」「同じノードを最低数分維持する」「連続して複数回失敗した場合だけ緊急切替する」といった条件を設けます。たとえば差が 30ms 未満なら変更しない、最後の切替から 180 秒は通常切替を抑制する、といったルールです。
API の自動運用では、スクリプトを cron やタスクスケジューラで定期実行する方法が簡単です。ただし毎分すべてのノードを測定すると、測定先やコアへ不要な負荷をかけます。候補を数個に絞り、測定間隔を用途に合わせ、エラー時の再試行にも上限を設けてください。動画、ゲーム、SSH、AI API は必要な安定性が異なるため、一つの万能グループで全通信を切り替えるより、用途別にポリシーグループを作る方が予期せぬセッション切断を減らせます。
ログ・障害対応・安全な長期運用
自動切替が動かないときは、まず「API に接続できない」「グループ名が違う」「候補ノードが存在しない」「遅延測定が失敗している」「PUT は成功したが通信ルールが別グループを使っている」のどこで止まったかを分けます。Clash のログとライブ接続を同じ時刻で確認し、対象通信が本当に切替対象のグループを通っているかを見ます。API から PROXY を変更しても、ルール側が AI_SERVICE や DIRECT を指定していれば、ブラウザの通信経路は変わりません。
設定更新後にノード名が変わった場合は、候補リストを再生成する処理も必要です。購読更新の直後だけ一時的にグループが空になるクライアントもあるため、更新イベントと自動切替を同時に走らせず、設定の読み込み完了を確認してから測定を開始します。失敗時には直前の選択先へ戻す、または安全な固定ノードへ退避する設計にすると、全候補の測定失敗がそのまま通信不能へつながる事態を避けられます。
また、external-controller のアドレスや secret を設定ファイルへ保存する場合は、ファイル権限を絞り、バックアップや同期サービスの対象を確認します。共有サーバー上でスクリプトを動かすなら、ローカルソケット、SSH ポートフォワーディング、VPN 内の限定アドレスなど、管理経路そのものを保護してください。自動化は便利ですが、認証情報を環境変数へ入れただけで完全に安全になるわけではありません。プロセス一覧、デバッグログ、CI の出力、シェル履歴まで含めて secret の露出経路を確認することが大切です。
GUI だけで切替する従来の方法は、状態を目で確認しやすい反面、外出先や常時稼働端末では手動操作が遅れ、Clash for Windows のように環境によっては更新が止まったクライアントへ依存することもあります。単純な固定スクリプトもノード名の変更や遅延の揺らぎに弱い一方、Clash V.CORE なら Mihomo 系コアの API、グループ管理、ログ確認を一つの運用にまとめ、external-controller を使った安全な自動切替を段階的に構築できます。この記事の構成を自分のルールと候補名へ置き換えたうえで、まずはローカル API と少数ノードの測定から始めたい方は、対応クライアントをまとめたダウンロードページを確認してください。