サブスクリプション全体とノードProviderを区別する
サブスクリプションの更新に失敗したら、まず何がダウンロードしているのか、何をダウンロードしているのかを確認しましょう。GUIクライアントのサブスクリプション更新と、mihomoのproxy-providersは別の仕組みです。前者は通常、設定ファイル全体を取り込み、後者はノード一覧だけを更新します。ノードに接続できても、その時点でサブスクリプションURLにアクセスできるとは限りません。サブスクリプションをダウンロードできても、含まれるノードがすべて使えるとは限りません。
- クライアントのサブスクリプション:
dns、proxy-groups、rulesなど、設定全体が含まれる場合があります。更新頻度はクライアントが管理します。画面上の自動更新設定は、コアのProviderタイマーとは別のものです。 - ノードProvider:mihomoが実行中に読み込みます。リモートの内容は、対応するノード一覧形式にする必要があります。この記事では、トップレベルに
proxies:を置くYAML形式を使用します。ポリシーグループからはuseで参照します。 - ヘルスチェック:テスト用URLにリクエストを送り、ノードへの到達性と遅延を確認します。サブスクリプションを再ダウンロードする機能ではなく、期限切れの認証情報を修復することもできません。
エラー別の切り分け:HTTP、形式、ノードの順に確認
更新に失敗したときのログを保存し、発生時刻、リクエスト先のドメイン、エラー末尾を記録します。画面の「更新に失敗しました」だけを撮影しても十分な情報は得られません。ログにサブスクリプションのクエリパラメーターが含まれる場合は、共有前にトークンを隠してください。以下のエラーは発生する段階が異なるため、同じ方法で対処できるとは限りません。
| ログ・状態 | 優先して確認する項目 | 次の手順 |
|---|---|---|
no such host |
サブスクリプションのドメイン解決 | getentを実行し、ドメイン名に誤りがないか確認 |
i/o timeout、context deadline exceeded |
接続経路、出口、タイムアウトの発生段階 | 直接接続とローカルプロキシをそれぞれテスト |
x509: certificate has expired |
ローカルの時計とサーバー証明書の有効期限 | まず時刻を合わせてから、サイトの証明書を確認 |
certificate signed by unknown authority |
システムのCA証明書、企業ネットワークのHTTPS検査 | 信頼できるルート証明書の入手元を確認 |
| HTTP 401 / 403 | トークン、アクセス制限、User-Agent | プロバイダーの案内とレスポンス本文を確認 |
| HTTP 429 | リクエスト頻度の制限 | Retry-Afterに従って待ち、更新間隔を延ばす |
yaml: unmarshal errors |
レスポンス形式、フィールドの型、インデント | 返された内容がHTMLやエラーメッセージではないか確認 |
タイムアウトはノード障害とは限らない
サブスクリプションのドメインには、特定のネットワークからしかアクセスできない場合や、プロキシの出口でリクエストが制限される場合があります。Linuxのターミナルで設定したHTTPS_PROXYと、デスクトップ環境のシステムプロキシは一致するとは限りません。クライアントの更新機能も、同じ設定を使うとは限りません。そのため、ブラウザーでサイトを開けても、バックグラウンドのサブスクリプション取得が同じ経路を通っているとは言えません。
以下のコマンドはサブスクリプションURLを読み込むため、トークンをシェルの履歴に直接残しません。レスポンスはアクセス制限のある一時ディレクトリに保存します。リダイレクトは自動で追わず、サブスクリプション本文も表示しません。301または302が返った場合は、ローカルで転送先を確認し、信頼できることを確かめてからアクセスしてください。
umask 077
workdir=$(mktemp -d)
read -r -s -p 'サブスクリプション URL: ' SUB_URL
printf '\n'
curl --noproxy '*' --connect-timeout 10 --max-time 30 \
-sS -D "$workdir/headers.txt" \
-o "$workdir/body.txt" \
-w 'HTTP=%{http_code} total=%{time_total}s\n' \
"$SUB_URL"
getent ahosts example.com
最後の行のexample.comは、サブスクリプションURLのホスト名に置き換えてください。プロトコルとトークンは含めず、ドメイン名だけを指定します。--noproxy '*'はcurlに明示的に設定されたプロキシを除外します。システムでTUNやルーターの透過プロキシが動作している場合、通信が引き続き中継される可能性があります。この結果だけで物理的な直接接続を証明することはできません。
コアがmixed-portで実際に7890を待ち受けていることを確認したら、明示的なHTTPプロキシ経由のリクエストと比較できます。以下のコマンドでは前の手順で設定した変数を引き続き使います。表示される結果は今回のリクエストの実際の状態であり、成功を前提としたものではありません。
curl --noproxy '' --proxy http://127.0.0.1:7890 \
--connect-timeout 10 --max-time 30 \
-sS -D "$workdir/proxy-headers.txt" \
-o "$workdir/proxy-body.txt" \
-w 'HTTP=%{http_code} total=%{time_total}s\n' \
"$SUB_URL"
# ローカルでの確認後、サブスクリプション情報を含む一時ファイルを削除する
unset SUB_URL
rm -rf -- "$workdir"
unset workdir
証明書エラー、User-Agent拒否、形式の不一致
証明書エラー:時刻を合わせ、検証を無効にしない
まずtimedatectl statusを実行し、日付、タイムゾーン、時刻同期の状態を確認します。証明書の有効期間は絶対時刻を基準に判定されます。タイムゾーンの表示が異なること自体は、証明書が期限切れになる原因ではありません。時刻に問題がなければ、システムのCA証明書パッケージがインストールされているか、企業ネットワークがHTTPS証明書を差し替えていないか、サブスクリプションサービスの証明書が期限切れになっていないかを確認してください。
DebianやUbuntuでは、ca-certificatesパッケージを確認できます。管理者から企業のルート証明書を受け取った場合は、ディストリビューションの手順に従ってインストールし、入手元を確認してください。curl -kや証明書検証の無効化を恒常的な対策にしないでください。サブスクリプションには接続情報が含まれるため、検証を無効にすると内容を改ざんされるリスクが高まります。
401・403エラー:認証情報を確認してからUser-Agentを確認
401はトークンの無効化で発生することがよくあります。403はプランの状態、IP制限、User-Agentポリシー、防御ページなどが原因になる場合もあります。プロバイダーからのレスポンスを確認し、403が出たからといってUser-Agentを何度も変えないでください。ブラウザーでHTTP 200が返っても、ログインページが表示されているだけで、有効なサブスクリプションではない場合があります。
プロバイダーがmihomoのUser-Agentを明示的に指定している場合は、Providerでリクエストヘッダーを設定できます。headerの各値には文字列のリストを指定します。以下はフィールドの記述例です。該当するHTTP Providerに追加してください。アカウントの認証を代替するものではなく、アクセス制御の回避に使ってはいけません。
header:
User-Agent:
- mihomo
解析エラー:拡張子だけでなく本文の構造を確認
サブスクリプションURLが.yamlで終わっていても、サーバーがYAMLを返すとは限りません。ローカルでレスポンスヘッダーと本文の冒頭を確認してください。HTMLの防御ページ、JSON形式のエラーメッセージ、Base64形式のノード一覧、設定ファイル全体、Provider用のノード一覧は、それぞれ異なるデータ形式です。HTTP 200はリクエストが成功したことを示すだけです。
この記事で使用するProviderの本文は、以下のトップレベル構造にする必要があります。例には実際のノードを含めていません。proxies: []はフィールドの型を示すだけです。実際のサービスでは、ノード一覧に内容が含まれている必要があります。設定ファイル全体をノードProviderとして使わないでください。特に、含まれるrulesがメイン設定に自動で統合されることはありません。
proxies: []
- メイン設定のProvider名は重複しないようにします。
airportのような、変更されにくい英語名がおすすめです。 - YAMLのインデントにはスペースを使い、Tabは使わないでください。特殊文字による誤認を避けるため、URLは引用符で囲むのがおすすめです。
- ノード形式の変換は信頼できる環境で行ってください。トークン付きのサブスクリプションを、提供元が不明な変換サイトに送信しないでください。
proxy-providersの定期更新:各フィールドと参照関係
以下の設定は各要素のつながりを示す例です。3600秒ごとにノード一覧を取得し、600秒ごとにヘルスチェックを実行します。ポリシーグループPROXYはuse: [airport]でノードを参照し、最後のルールで未一致の通信をこのグループに送ります。すぐに使えるサブスクリプションではありません。example.comはドキュメント用のドメインなので、実際に使う前にプロバイダーが指定するProviderのURLに置き換えてください。
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
proxy-providers:
airport:
type: http
url: "https://example.com/subscription.yaml"
path: ./providers/airport.yaml
interval: 3600
health-check:
enable: true
url: "https://www.gstatic.com/generate_204"
interval: 600
timeout: 5000
lazy: true
proxy-groups:
- name: PROXY
type: select
use:
- airport
rules:
- MATCH,PROXY
| フィールド | 設定例 | 役割・単位 |
|---|---|---|
interval |
3600 | リモートProviderの更新間隔(秒) |
path |
./providers/airport.yaml |
コアの作業ディレクトリからの相対パスで指定するキャッシュ先 |
health-check.interval |
600 | ノードのヘルスチェック間隔(秒) |
health-check.timeout |
5000 | 1回のチェックのタイムアウト(ミリ秒) |
health-check.lazy |
true | 未使用時の定期チェックを省略して、プローブを減らす |
health-check.urlには、安定して期待どおりのレスポンスを返す軽量なURLを指定します。上記は一般的な例ですが、すべてのネットワーク出口からアクセスできるとは限りません。テスト先に障害があると、正常なノードが一斉に異常と表示されることがあります。その場合はノードをまとめて削除するのではなく、まずテスト先の問題を切り分けてください。
更新リクエストの通信経路
現在のmihomoでは、HTTP Providerのproxyフィールドを使って、ダウンロード時に使うプロキシまたはポリシーグループを指定できます。指定しない場合、デスクトップのシステムプロキシが引き継がれるとは限りません。サブスクリプションへのアクセスにプロキシが必要な場合は、初回ダウンロードより前に、指定した出口が単独で利用できることを確認してください。
更新間隔を短くしすぎないでください。3600秒は1時間なので、通常は毎分ポーリングするより適しています。適切な間隔はプロバイダーの制限に従ってください。ヘルスチェックではノードごとにリクエストが発生する場合があり、ノード数が多いほどリクエスト総数も増えます。チェック間隔を短くしても、サブスクリプションの更新が速くなるわけではありません。
設定を反映する:検証、権限、再起動の順序
まず現在の実行方法を確認します。GUIクライアントが生成した実行用設定は、次回のサブスクリプション切り替え時に手動変更が上書きされることがあります。クライアントが対応する永続化用のマージ機能を使ってください。mihomoを単独で実行している場合は、実際の起動引数にある-dと-fを基準にします。読み込まれていない同名ファイルを変更しないでください。
- 現在の設定をバックアップし、復元できる状態にしておきます。
- Providerとポリシーグループを一緒にメイン設定へ追加し、
useの値がProvider名と完全に一致していることを確認します。 - キャッシュ用ディレクトリを作成し、コアの実行ユーザーに書き込み権限があることを確認します。
- 設定を検証し、問題がなければサービスを再起動します。
- 起動時とその後の更新ログを確認し、ダウンロード、解析、権限に関するエラーがないことを確認します。
ここでは、作業ディレクトリを~/.config/mihomo、設定ファイルをconfig.yamlとし、mihomo.serviceという名前のsystemdユーザーサービスを作成済みとします。システムサービスを使う場合は、実際の環境に合わせて設定ディレクトリとコマンドのユーザー範囲を調整してください。
cp ~/.config/mihomo/config.yaml \
~/.config/mihomo/config.yaml.bak
mkdir -p ~/.config/mihomo/providers
chmod 700 ~/.config/mihomo/providers
chmod 600 ~/.config/mihomo/config.yaml
mihomo -t -d ~/.config/mihomo \
-f ~/.config/mihomo/config.yaml
# 検証に成功した場合のみ実行する
systemctl --user restart mihomo.service
journalctl --user -u mihomo.service -n 100 --no-pager
mihomo -tは設定を検証するコマンドです。リモートの内容やすべてのノードが動作することまでは確認できません。設定の検証に成功しても更新に失敗する場合は、実際の実行ログを確認してください。サービスにpermission deniedが表示されたら、ディレクトリの所有者、サービスの実行ユーザー、キャッシュ先を確認します。設定ディレクトリ全体にすべてのユーザーの書き込み権限を付与しないでください。
Providerのキャッシュが残っていれば、更新に失敗しても古いノードを使える場合があります。画面にノードが表示されていても、更新に成功したとは限りません。ログとstat ~/.config/mihomo/providers/airport.yamlを併せて確認し、キャッシュの状態を調べてください。更新日時は参考情報にすぎず、それだけでリモートの内容が変わったことを証明するものではありません。
よくある質問:自動更新が反映されないように見える理由
intervalを設定した場合、クライアントを終了しても更新されますか?
このYAML設定だけで、終了したコアが自動的に起動することはありません。Providerの定期更新にはmihomoが継続して動作している必要があり、PCのスリープ、サービスの終了、ネットワーク切断によって更新が実行されないことがあります。Linuxで常時稼働させる場合は、導入方法に応じてsystemdを設定してください。一度起動に成功しただけで、OS起動時の自動起動まで設定できたと判断しないでください。
サブスクリプションは更新されたのに、ルールやDNSが変わらないのはなぜですか?
proxy-providersが更新するのはノード一覧であり、メイン設定全体ではありません。ルールの取得には別途rule-providersを使えますが、ルールセットもrulesから参照する必要があります。設定全体を置き換えたい場合は、クライアントのサブスクリプション更新機能を使い、ローカルの上書き設定の優先順位を確認してください。各フィールドの関係は設定リファレンスをご覧ください。
ヘルスチェックに成功したのに、特定のサイトを開けないのはなぜですか?
ヘルスチェックで確認できるのはテスト先だけで、すべてのサイトの接続性が保証されるわけではありません。特定のサイトには、DNSの名前解決、ルールの適用先、出口の地域、接続先サービスの制限なども影響します。まず実際に選ばれたポリシーグループとノードを確認し、その後で対象ドメインを調べてください。プローブが一度成功しただけで、通信経路全体に問題がないと判断しないでください。
問題が解決したことをどう確認できますか?
- サブスクリプションのリクエストがログインページやエラー本文ではなく、有効な内容を返す。
- 実行ログにProviderの解析エラーがなく、ポリシーグループに想定したノードが表示される。
- 古いキャッシュがない初回起動でも、サブスクリプションを取得できる。
- 設定した更新間隔が経過した後、次の更新が実行されたことを確認する。
- クライアントの再起動後も設定が保持され、トークンやノードの認証情報が公開ログに含まれていない。
調査の順番は固定します:ダウンロード経路 → HTTPステータス → 内容の形式 → Providerの参照 → 定期更新 → ノードの確認。この順序なら、「サブスクリプションを取得できない」問題と「ノードを使えない」問題を分けられます。原因を特定しないままクライアントを何度も再インストールするのも避けられます。初めて設定する場合は、まずクイックスタートに沿って最小限の設定を行い、自動更新を順に追加してください。