Clash APIでノード切り替えを自動化する考え方
プロキシの品質は、回線混雑、時間帯、接続先の地域、ノード側の負荷によって変化します。朝は快適だったノードが夜になるとパケットロスを起こしたり、TLS 接続は成功しても API の応答だけが極端に遅くなったりするため、固定のノードを選び続ける運用には限界があります。Clash の画面を開いて手動で選び直す方法は一時的な対処としては便利ですが、長時間動くバッチ、開発用 CLI、定期取得スクリプト、複数端末の運用では判断が遅れます。
そこで利用できるのが、Clash または Mihomo が提供する外部コントローラー APIです。API を有効にすると、現在のプロキシグループ、所属ノード、接続中の選択先、遅延測定結果などをプログラムから取得できます。さらに、グループに対して別のプロキシを選択するリクエストを送れば、GUI を操作せずに出口を変更できます。構成としては、Python が一定間隔でノードを調べ、条件を満たす候補を選び、Clash API へ切り替え命令を送る流れです。
重要なのは、単純に「最も小さい遅延のノード」を常に採用すればよいわけではない点です。測定先が近すぎると実際の利用サービスとの相関が弱く、短時間の瞬間値だけで切り替えるとノードが頻繁に入れ替わります。また、認証中やストリーミング中に出口 IP が変わると、セッションが切れたり、サービス側のリスク判定に影響したりすることがあります。したがって、遅延だけでなく、連続失敗回数、最低保持時間、切り替え間隔、ノードの用途を組み合わせる必要があります。
外部コントローラー API の安全な準備
Clash の設定では、通常 external-controller と secret を指定します。クライアントやコアによって設定画面の名前は異なりますが、API の待受先を 127.0.0.1:9090 のようなループバックアドレスにし、認証用の長いランダム文字列を設定する考え方は共通しています。すでに別の管理画面や開発ツールが同じポートを使っている場合は、起動ログでポート競合が出ていないか確認してください。
API を LAN やインターネットへ直接公開する構成は避けるべきです。待受アドレスを 0.0.0.0 にすると、ファイアウォールの設定次第では同じネットワーク上の端末からノード選択を変更できる状態になります。リモート管理が必要な場合でも、まず VPN や SSH ポートフォワードなど認証済みの管理経路を用意し、API 自体を無防備に公開しない構成にしてください。API の秘密鍵は購読 URL と同じく、ログ、スクリーンショット、公開リポジトリへ残さないことが大切です。
Python からは HTTP クライアントを使って API に接続します。最初の疎通確認では、いきなり切り替え命令を送らず、現在のプロキシ一覧を取得できるかだけを確認します。コントローラーのバージョンやクライアントの実装によって、パスや返却される JSON の細部が異なる場合があるため、手元の環境でレスポンスを一度保存してからコードを書くと安全です。
import os
import requests
API_BASE = os.getenv("CLASH_API", "http://127.0.0.1:9090")
API_SECRET = os.environ["CLASH_SECRET"]
headers = {
"Authorization": f"Bearer {API_SECRET}",
"Content-Type": "application/json",
}
response = requests.get(
f"{API_BASE}/proxies",
headers=headers,
timeout=5,
)
response.raise_for_status()
proxies = response.json()["proxies"]
print(sorted(proxies.keys())[:10])
ここで HTTP 401 が返る場合は、秘密鍵の値だけでなく Bearer の付け方、環境変数の読み込み元、対象ポートの確認が必要です。404 の場合は、Clash API ではなく別のサービスへ接続している可能性があります。タイムアウトなら、コアが停止している、ポートが違う、OS のファイアウォールが遮断している、あるいは API が TLS 付きの別 URL で待ち受けている可能性を順番に調べます。
遅延・死活確認・失敗回数を組み合わせる
ノードの死活確認には、Clash API の遅延測定機能を使う方法と、実際の利用先へ小さな HTTPS リクエストを送る方法があります。前者はノードごとの比較がしやすく、設定が簡単です。後者は自分の用途に近い結果を得やすい一方、接続先の障害やレート制限まで測定結果へ混ざります。候補の比較には安定した測定 URL を使い、最終確認には自分の運用に近い軽量な URL を使うなど、役割を分けると判断しやすくなります。
測定結果は一回だけで判断しないでください。たまたま DNS が遅れた、測定先の CDN が混雑していた、TCP 接続だけ再送されたという理由で、一度の高遅延を障害と誤認することがあります。たとえば直近三回のうち二回以上がしきい値を超えた場合だけ不健全と判定し、連続して失敗したノードには一定時間のクールダウンを設定します。これにより、正常なノードが一時的な揺らぎで候補から外れ続ける現象を減らせます。
自動切り替えでは、次のような条件を分けて管理すると運用しやすくなります。
- 応答時間:一定のタイムアウト以内に接続できるか、中央値が許容範囲に収まっているか。
- 連続失敗:一回の失敗では切り替えず、二回または三回連続した場合に異常とするか。
- 最低保持時間:切り替え後の数分間は再判定を抑制し、短時間の往復を防ぐか。
- 候補除外:直前に失敗したノード、用途が異なるノード、帯域制限のあるノードを除外するか。
- 復帰条件:失敗したノードが一回成功しただけで戻すのか、複数回の成功を待つのか。
API の遅延測定を使うコードは、実装差を吸収できるように例外処理を広めに取ります。グループ名には日本語や空白が含まれることがあるため、URL へ埋め込むときは必ず URL エンコードしてください。API が返すノード名をそのままログへ出す場合も、購読サービスの識別情報が含まれていないか確認します。
import time
from urllib.parse import quote
GROUP = "AUTO"
TEST_URL = "https://www.gstatic.com/generate_204"
TIMEOUT_MS = 5000
def measure(proxy_name):
encoded = quote(proxy_name, safe="")
url = (
f"{API_BASE}/proxies/{encoded}/delay"
f"?timeout={TIMEOUT_MS}&url={quote(TEST_URL, safe='')}"
)
try:
result = requests.get(url, headers=headers, timeout=8)
result.raise_for_status()
return int(result.json().get("delay", 99999))
except (requests.RequestException, ValueError, TypeError):
return 99999
def choose_best(candidates, current):
samples = {name: measure(name) for name in candidates}
healthy = {
name: delay for name, delay in samples.items()
if delay < 1200
}
if not healthy:
return None
best = min(healthy, key=healthy.get)
if best == current:
return current
time.sleep(1)
second_delay = measure(best)
return best if second_delay < 1200 else None
実運用では、候補名をコードへ固定するより、対象グループの JSON から選択可能なプロキシを読み取る方法が柔軟です。ただし、グループ自身、DIRECT、REJECT、別の子グループなどが同じ一覧に現れることがあります。自動切り替え対象は名前の接頭辞や明示的な許可リストで絞り込み、意図しないグループを再帰的に切り替えないようにしてください。
安全な切り替え処理と運用設計
ノードを選んだら、グループの選択先を変更する API リクエストを送ります。一般的には対象グループへ新しいプロキシ名を JSON で送る形式ですが、実装によってパスや許容される値が異なるため、まず現在のグループ情報を取得し、GUI で手動変更したときの API 動作をログで確認してください。切り替え後は、すぐに成功と判断せず、数秒待ってからもう一度測定し、Clash の接続ログで新しい出口が使われているか確認します。
def switch_group(group_name, proxy_name):
endpoint = f"{API_BASE}/proxies/{quote(group_name, safe='')}"
response = requests.put(
endpoint,
headers=headers,
json={"name": proxy_name},
timeout=5,
)
response.raise_for_status()
current = "Node-A"
target = choose_best(["Node-A", "Node-B", "Node-C"], current)
if target and target != current:
switch_group(GROUP, target)
print(f"switched to {target}")
この処理を cron、タスクスケジューラ、systemd timer などで定期実行する場合、毎回同じログを大量に出さない工夫が必要です。現在の選択先、判定理由、測定値、切り替え前後、エラー内容を一行の構造化ログとして保存すると、あとから「API が落ちた」のか「ノードだけ遅かった」のかを追跡できます。秘密鍵、購読 URL、完全な接続先 URL はログへ書かず、必要ならノード名を一部マスクしてください。
また、切り替え直後に既存接続がすべて新ノードへ移るとは限りません。すでに確立した TCP セッションや長時間の WebSocket は旧経路を維持し、新しいリクエストから新しい選択先を使うことがあります。そのため、ノード変更を行った瞬間にアプリケーションの処理を強制終了する設計は避け、再試行が安全な処理だけに限定してください。認証、決済、ファイル更新など二重実行が問題になる処理では、切り替えを自動化する前にアプリケーション側の冪等性を確認します。
さらに、Python スクリプトが停止したときの退避先も決めておきます。自動化プロセスが API の応答を受け取れない状態で、無理に切り替えを繰り返すのは危険です。一定回数失敗したら処理を終了し、Clash 側で最後に選ばれていたノードを維持する、または管理者が確認しやすい固定グループへ戻す、といった保守的な動作が適しています。自動化は「常に最速にする」よりも「異常時に予測できる状態を保つ」ことを優先してください。
動かないときの確認順序
API 自動切り替えが動かない場合は、Python のロジックから疑うのではなく、層を分けて確認します。まず Clash のコアが起動しているか、次に外部コントローラーのポートへ接続できるか、その後に認証が通るかを確認します。認証後に /proxies の一覧が読めれば API の基本経路は成立しています。そこで対象グループの名前、候補ノード名、切り替え権限のあるエンドポイントを確認し、最後に遅延測定の URL としきい値を調整します。
- 401:Bearer トークン、秘密鍵、環境変数、接続先ポートを確認する。
- 404:Clash API ではないサービスへ接続していないか、パスの実装差を調べる。
- 空の候補:対象グループが選択可能なノードを返しているか、DIRECT などを除外しすぎていないか確認する。
- 測定値が常に大きい:テスト URL、DNS、TLS、ノード側の接続制限を分離して調べる。
- 変更が反映されない:グループ名のエンコード、JSON のキー名、既存接続の残留を確認する。
最後に、GUI の画面表示と API のレスポンスが一致しているかを見ます。画面上ではグループが切り替わっていても、スクリプトが監視しているのは別の親グループかもしれません。逆に API では変更成功と返っても、ルールが別のグループを参照していれば、実際の接続先は変わりません。Connections、Logs、Proxies の三つを同じ時刻で照合し、変更前後のホストとポリシーを記録すると、設定ミスとノード障害を分離しやすくなります。
GUI だけで運用する方法は導入が簡単ですが、複数ノードの履歴比較、失敗回数の管理、クールダウン、用途別の切り替え条件まで扱うには機能が不足しがちです。一方、手作りの Python スクリプトだけでは認証管理や例外処理、クライアントごとの API 差分に手間がかかります。Clash V.CORE なら、GUI で現在の状態を確認しながら API とルールを組み合わせ、ノードの死活監視と安全な自動切り替えを一つの運用へまとめやすくなります。まずは手動選択できる小さなグループで試し、ログと退避条件を整えてから、必要に応じてダウンロードページから導入を始めてください。
// エディターズ・チョイス
Clash V.COREでノード管理を効率化
API、ログ、プロキシグループを確認しながら、自動切り替えの土台を整えられます。
- プロキシグループを見やすく管理
- API連携の検証を始めやすい構成
- 接続ログとノード状態を確認
- 手動切り替えから段階的に自動化
- 複数ノードの用途別運用に対応