Clash APIで実現する自動ノード切り替え設計と実装ガイド
接続先のタイムアウトや急な遅延を手作業で復旧するのではなく、ClashのAPIからプロキシ状態を取得し、条件に応じてグループを制御する仕組みを構築します。開発環境や常時稼働サーバーで使える監視設計と安全な運用方法を紹介します。
Clash APIで自動切り替えを行う考え方
ノードの自動切り替えは、単純に「一番速いノードを常に選ぶ」仕組みではありません。実際の通信では、瞬間的な遅延、接続拒否、DNSの失敗、経路上のパケットロスが混在します。1回だけ測定した結果で切り替えると、測定対象の一時的な混雑に反応してノードが頻繁に変わるため、利用中の接続が切れたり、アプリケーションのログイン状態が失われたりします。安定した設計では、Clashの外部コントローラーAPIから現在のプロキシ状態を取得し、複数回の測定結果としきい値を組み合わせて、必要なときだけ策略グループを変更します。
対象となるのは、Clash Verge、Clash Verge Rev、Mihomo系クライアントなど、外部コントローラーを有効化できる環境です。クライアントの設定画面や設定ファイルでAPIポートを確認し、必要に応じて次のような項目を用意します。ポート番号やsecretは環境ごとに異なるため、以下は説明用の値です。
external-controller: 127.0.0.1:9090
secret: "change-this-to-a-long-random-token"
external-controller はAPIが待ち受けるアドレスとポート、secret はAPIへのアクセスを認証するトークンです。ローカルの監視スクリプトだけが使うなら、待ち受けアドレスは 127.0.0.1 に限定するのが安全です。LAN全体から接続できるアドレスに変更する場合は、ファイアウォール、アクセス元、認証トークンを必ず確認してください。APIポートをインターネットへ直接公開する構成は避けます。
secretはサブスクリプション情報と同じように扱う
外部コントローラーのsecretを知られると、設定の取得、プロキシグループの変更、接続の切断などを操作される可能性があります。設定ファイルを公開リポジトリへ保存せず、スクリプト内に固定する場合もアクセス権を制限してください。共有端末では、環境変数やOSの秘密情報ストアを使う方法が適しています。
状態取得とグループ変更に使うAPI
自動切り替えで中心になるAPIは、プロキシ一覧の取得、特定プロキシの遅延測定、選択型グループの切り替えの3つです。Clash系カーネルやクライアントのバージョンによって利用できる項目に差があるため、まず現在の環境で応答を確認してから実装を進めます。
| 用途 | メソッドとパス | 確認する内容 |
|---|---|---|
| プロキシ一覧 | GET /proxies | ノードと策略グループの名前、現在の選択先、遅延履歴 |
| グループの詳細 | GET /proxies/{name} | 対象グループの種類、現在選択されているプロキシ |
| 遅延測定 | GET /proxies/{name}/delay | 指定URLへの接続遅延と測定成否 |
| 選択先の変更 | PUT /proxies/{name} | select系グループの現在のプロキシを変更 |
APIの認証には通常、HTTPヘッダー Authorization: Bearer トークン を付けます。グループ名やノード名には空白、日本語、記号が含まれることがあるため、URLのパスへ直接連結せず、必ずURLエンコードしてください。現在の構成を調べるだけなら、次のコマンドで十分です。
curl -H "Authorization: Bearer change-this-to-a-long-random-token" \
http://127.0.0.1:9090/proxies
応答には proxies オブジェクトが含まれ、各要素に type、name、now、all などの情報が入ります。実装では表示名ではなく、応答に含まれる正確な名前を利用します。例えばグループ名が「自動選択」、ノード名が「東京 01」の場合、余分な空白を削除したり、独自の短縮名へ変換したりすると切り替えに失敗します。
選択型グループを変更するリクエストは次の形式です。これはノードそのものを変更する操作ではなく、指定したグループの現在の選択先を変更する操作です。
curl -X PUT \
-H "Authorization: Bearer change-this-to-a-long-random-token" \
-H "Content-Type: application/json" \
-d '{"name":"東京 01"}' \
"http://127.0.0.1:9090/proxies/%E8%87%AA%E5%8B%95%E9%81%B8%E6%8A%9E"
グループの種類を先に確認する
select のような手動選択型グループはAPIから選択先を変更できます。一方、url-test、fallback、load-balance はカーネル自身が選択を管理するため、外部スクリプトから無理に上書きすると本来の動作と競合します。まずは手動選択グループを対象にし、自動グループは状態監視だけに留める構成が安全です。
切り替え条件を設計する:速度より安定性を優先する
監視処理を作る前に、何を異常と判定するかを決めます。HTTPの応答時間だけを見ていると、測定先のCDNや地域差の影響を強く受けます。反対に、pingだけではTCP接続やTLSハンドシェイクの失敗を検出できない場合があります。実際の利用に近いHTTPS URLを一つ以上使い、接続成功、タイムアウト、応答時間を組み合わせて評価してください。
- 監視対象のグループから、候補ノードの一覧を取得します。DIRECT、REJECT、別のグループ名は候補から除外します。
- 各ノードへ同じHTTPS URLを使って遅延を測定します。測定URLは短い応答を返し、リダイレクトやログインを必要としないものを選びます。
- 1回の失敗だけで切り替えず、連続2〜3回の失敗、または設定したタイムアウト超過を異常と判定します。
- 現在のノードが異常になった場合だけ、候補の中から一定回数成功したノードを選びます。
- 切り替え後はクールダウン時間を設け、短時間に再度切り替えないようにします。
例えば、タイムアウトを5000ミリ秒、異常判定を3回連続失敗、復帰判定を2回連続成功、切り替え後のクールダウンを60秒とします。この組み合わせなら、一時的なパケットロスでは切り替わりにくく、明らかな接続不能には比較的早く対応できます。仕事中のビデオ会議や長時間のダウンロードでは、切り替え速度よりも既存接続を不用意に壊さないことが重要です。
- 遅延のしきい値: 例として1000〜1500ミリ秒を警告ラインにします。ただし、回線や地域によって基準は変わるため、平常時の中央値を基準にします。
- 連続失敗回数: 1回ではなく2〜3回を要求します。測定先の一時的な応答遅延を異常と誤認しにくくなります。
- ヒステリシス: 現在のノードより少し速いだけの候補へは移動しません。例えば新候補が現在値より30%以上速い場合だけ切り替える方法があります。
- クールダウン: 切り替え直後は60〜180秒ほど再判定を抑制し、ノード間の往復を防ぎます。
候補を選ぶときは最小遅延だけでなく、測定成功率も加味します。5回中4回成功したノードより、5回すべて成功して少し遅いノードの方が、長時間の利用では実用的なことがあります。履歴をメモリや小さなJSONファイルに保存し、直近5〜10回の中央値を使うと、単発の異常値に引きずられません。
Pythonで作る最小監視スクリプト
次の例はPython標準ライブラリだけで、APIからグループ情報を取得し、候補ノードの遅延を測定して、条件を満たした場合に選択先を変更する基本形です。実際の環境では、グループ名、測定URL、secret、候補除外条件を自分の設定に合わせて変更してください。
import json
import os
import time
import urllib.parse
import urllib.request
API = "http://127.0.0.1:9090"
TOKEN = os.environ["CLASH_SECRET"]
GROUP = "自動選択"
TEST_URL = "https://example.com/"
TIMEOUT = 5000
def request(path, method="GET", payload=None):
headers = {"Authorization": f"Bearer {TOKEN}"}
data = None
if payload is not None:
headers["Content-Type"] = "application/json"
data = json.dumps(payload, ensure_ascii=False).encode()
req = urllib.request.Request(
API + path, data=data, headers=headers, method=method
)
with urllib.request.urlopen(req, timeout=8) as response:
return json.loads(response.read().decode())
def delay(name):
path = "/proxies/" + urllib.parse.quote(name, safe="")
query = "?timeout={}&url={}".format(
TIMEOUT, urllib.parse.quote(TEST_URL, safe="")
)
result = request(path + "/delay" + query)
return int(result["delay"])
def choose(name):
path = "/proxies/" + urllib.parse.quote(GROUP, safe="")
request(path, method="PUT", payload={"name": name})
info = request("/proxies")
group = info["proxies"][GROUP]
current = group.get("now")
candidates = [
item for item in group.get("all", [])
if item not in {"DIRECT", "REJECT"} and item != GROUP
]
best = None
for name in candidates:
try:
value = delay(name)
if best is None or value < best[1]:
best = (name, value)
except Exception as error:
print("測定失敗:", name, error)
if best and best[0] != current:
choose(best[0])
print("切り替え:", current, "→", best[0], best[1], "ms")
else:
print("変更なし:", current)
このコードは毎回最速の候補へ移動するため、そのまま常駐させると切り替えが多すぎます。実運用では、現在ノードの失敗回数を記録し、異常判定が成立した場合にだけ候補を比較します。また、切り替え対象のグループが存在しない、all が空、APIが401を返す、測定結果に delay が含まれない、といったケースを明示的に処理してください。
スクリプトを常時稼働させるときの補強
常時稼働サーバーでは、監視スクリプト自身が停止しても気づけるように、systemd、タスクスケジューラ、コンテナの再起動ポリシーなどを利用します。ただし、再起動するたびに直ちに切り替えるのではなく、起動後に現在の選択先を取得し、少なくとも数回測定してから判断してください。APIが一時的に利用できないときは、現在のグループ選択を変更せず、ログへ記録して次の周期に再試行します。
ログには測定時刻、対象グループ、現在のノード、候補ノード、測定値、切り替え理由を残します。secret、サブスクリプションURL、完全な設定内容はログへ書き出さないでください。ログが大量にならないよう、正常時は要約だけ、異常時は詳細を残す方式が扱いやすいです。通知を追加する場合も、切り替えのたびに送信するのではなく、一定時間内に複数回発生した場合や全候補が失敗した場合に限定します。
安全な運用と検証のチェックポイント
自動切り替えは便利ですが、APIに接続できることと、通信全体が正常であることは同じではありません。APIが200を返しても、選択したノードのTLS接続、DNS解決、TUN経由のアプリ通信が正常とは限りません。導入時はまず手動でグループを切り替え、ブラウザ、開発ツール、長時間接続を使って実際の通信を確認します。
- 対象範囲を限定する: 最初は一つのselectグループだけを監視し、DNSグループや他の重要なグループを変更しないようにします。
- APIをローカルに閉じる:
127.0.0.1バインドを優先し、LAN公開が必要な場合はファイアウォールで送信元を限定します。 - 手動操作を優先する: クライアント画面でユーザーが選択した直後は、クールダウンを開始し、監視処理が即座に元へ戻さないようにします。
- 異常時にDIRECTへ落とさない: すべての候補が失敗した場合、勝手に直接接続へ変更するのではなく、現在の選択を維持して通知する方が意図しない経路変更を防げます。
- カーネル差を確認する: APIの応答項目や遅延測定の挙動は、無印Clashとmihomo、さらにクライアントの実装によって異なる場合があります。
段階的に有効化する
最初の段階ではAPIから状態を取得してログへ記録するだけにし、次に通知、最後に自動切り替えを有効化します。読み取り専用の監視で候補名、遅延値、タイムアウトの頻度を把握してから書き込み操作を許可すれば、設定ミスによる連続切り替えや意図しない経路変更を発見しやすくなります。
最終的な目標は、最速値を追い続けることではなく、必要なときにだけ通信経路を回復させることです。APIの認証、候補の除外、連続失敗判定、ヒステリシス、クールダウン、十分なログを組み合わせれば、開発環境でも常時稼働サーバーでも、予測しやすい自動ノード切り替えを構築できます。
Clash クライアントをダウンロード
通信振り分けを行うには、まずクライアントが通信を引き継ぐ必要があります。ダウンロードセンターでお使いのプラットフォームのクライアントを選び、チュートリアルに戻ってシステムプロキシまたは TUN の引き継ぎを完了させてください。