まず明示的にプロキシを指定したリクエストで問題の切り分けを行い、その後にシステムプロキシやDNSを確認します。クライアントに「接続済み」と表示されても、通常はコアやトンネル機能が起動したことを示すだけです。ブラウザーの通信がコアを経由していることや、現在のノードから目的のサイトにアクセスできることまでは保証しません。「待ち受けポート → 明示的なリクエスト → アプリのプロキシ設定 → DNS → ルールとノード → TUN」の順に確認し、一度に変更する項目は一つに絞りましょう。
1. 画面が起動しているだけでなく、コアが待ち受け中か確認する
まずプロセスとポートを確認します。以下の2つのコマンドは状態を読み取るだけで、設定は変更しません。ssでプロセス情報を表示するには管理者権限が必要な場合があります。権限がなくても、待ち受けアドレスとポートは確認できます。
pgrep -af 'mihomo|clash'
sudo ss -lntp 'sport = :7890'
- 待ち受けが見つからない:コアの起動失敗、設定の解析エラー、または実際のポートが7890以外でないか確認します。
127.0.0.1:7890で待ち受けている:ローカルのプログラムからは接続できますが、LAN内の他の端末はこの入口を直接利用できません。0.0.0.0:7890で待ち受けている:IPv4のすべてのローカルインターフェースで接続を受け付けます。LANからのアクセス権限とファイアウォールも確認してください。- 別のプロセスがポートを使用中:まずプロセスの所有元を確認し、競合しているインスタンスを停止します。プロキシ関連のプロセスをまとめて終了させないでください。
mixed-portはHTTPとSOCKSの両方を受け付けます。設定でportまたはsocks-portだけを使用している場合は、プロトコルに対応するポートを指定してください。external-controllerは管理用インターフェースであり、ブラウザー用のプロキシポートではありません。
# このディレクトリの設定だけを確認します。常駐インスタンスは起動しません
mihomo -t -d "$HOME/.config/mihomo"
テストに成功しても、その設定をサービスが実際に使用しているとは限りません。独自に設定したsystemdユーザーサービスの場合は、次のコマンドで起動引数を確認できます。システムサービスではsudo systemctlを使い、--userを外してください。
systemctl --user cat mihomo.service
systemctl --user status mihomo.service --no-pager
journalctl --user -u mihomo.service -b -n 80 --no-pager
サービスが見つからない場合は、GUIクライアントのコアログを確認してください。トラブルシューティングのために同じポートで別のサービスを起動しないでください。address already in use、設定の解析失敗、権限不足、Providerの読み込みエラーを重点的に確認します。
2. curlでプロキシを明示し、アプリとコアの問題を切り分ける
まずHTTPプロキシの入口をテストします。--noproxy ""は今回のリクエストでプロキシ除外リストを空にし、環境変数のNO_PROXYによって意図せず直接接続されるのを防ぎます。比較しやすいよう、例では同じHTTPSアドレスを使っています。実際の調査では、開けない対象にも同じテストを実行してください。
curl --noproxy "" -x http://127.0.0.1:7890 \
--connect-timeout 5 --max-time 20 \
-v -o /dev/null https://example.com/
| 結果 | 説明 | 次の手順 |
|---|---|---|
Connection refused | ローカルTCP接続が拒否された | 待ち受けポートとアドレスを確認 |
407 | プロキシで認証が求められる | 設定の認証情報とアプリの認証情報を確認 |
| CONNECT確立後にタイムアウトした | プロキシ入口は応答したが、その先の通信が完了していない | ルール、ノード、接続先への通信ログを確認 |
certificate verify failed | TLS証明書の検証に失敗した | システム時刻、証明書ストア、中継プロキシを確認 |
| 接続先からHTTPレスポンスを受信した | このリクエスト経路では通信が往復できている | ブラウザーとシステムのプロキシ設定を確認 |
HTTP 403や429は、接続先または中継サービスからレスポンスが返ってきたことを示します。ポートの障害と即断しないでください。一度成功しても、確認できたのはその接続先とルール経路が使えたことだけです。すべてのサイト、UDP、IPv6が正常とは限りません。
プロキシを明示しないリクエストも比較する
curl --proxy "" --noproxy "*" \
--connect-timeout 5 --max-time 20 \
-I https://example.com/
このコマンドはcurlの明示的なプロキシ設定を無効にしますが、システム上のTUN、ポリシールーティング、透過プロキシは回避しません。本当に直接接続した場合と比べるには、元に戻せる環境でTUNを一時的に無効にしてから、テストを繰り返してください。明示的なプロキシでは成功し、ブラウザーで失敗する場合は、すぐにDNSを変更せず、次のセクションを確認します。
3. システムプロキシ、ブラウザー設定、端末の環境変数を確認する
Linuxデスクトップのプロキシ、端末の環境変数、ブラウザー独自の設定は、それぞれ別の接続経路です。どれか一つを有効にしても、ほかが自動で有効になるわけではありません。GNOMEデスクトップの場合は、「設定」→「ネットワーク」→「ネットワークプロキシ」で「手動」を選び、HTTPとHTTPSのプロキシホストを127.0.0.1、ポートを7890にします。ディストリビューションによって画面の名称は多少異なります。
HTTPS欄は「HTTPSリクエストに使用するプロキシ」を指定する項目で、プロキシのプロトコルをHTTPSにするという意味ではありません。ローカルのmixed-portを使う場合、端末のhttps_proxyにも通常はhttp://127.0.0.1:7890を指定します。
# 現在のshellのプロキシ環境変数を確認します。出力に認証情報が含まれる場合があるため公開しないでください
env | grep -iE '^(http|https|all|no)_proxy='
# このcurlリクエストだけにプロキシを設定します
http_proxy=http://127.0.0.1:7890 \
https_proxy=http://127.0.0.1:7890 \
curl --noproxy "" --max-time 20 -I https://example.com/
プロキシ除外リストと二重の通信経路を確認する
NO_PROXY=*を設定すると、この環境変数に対応するプログラムはプロキシを経由しません。テスト時は値を空にし、ローカルやイントラネットの例外を一つずつ戻してください。- Firefoxでは、「設定」→「一般」→「ネットワーク設定」→「設定」から、システムプロキシを使うか手動で設定するかを確認できます。拡張機能がこの設定を変更している場合もあります。
- 以前から開いているshell、起動済みのIDE、バックグラウンドサービスは、新しい端末に設定した環境変数を自動では引き継ぎません。変更後は対象プロセスの環境で改めて確認してください。
- コンテナー内の
127.0.0.1はコンテナー自身を指し、ホストではありません。ホスト側のポートが正常でも、コンテナーから接続できるとは限りません。
古い環境変数の影響が疑われる場合は、次のコマンドで一時的にクリーンなテスト環境を起動できます。影響するのは今回起動する子プロセスだけで、shell設定ファイルの永続的な設定は削除されません。
env -u http_proxy -u https_proxy -u all_proxy -u no_proxy \
-u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY -u NO_PROXY \
curl --noproxy "" -x http://127.0.0.1:7890 \
--max-time 20 -I https://example.com/
4. システムDNS、コアのDNS、fake-ipを切り分ける
ドメイン名を解決できない原因が、必ずしもシステムDNSにあるとは限りません。HTTP CONNECTでは接続先のホスト名をプロキシに渡せます。SOCKSでは、オプションによってどちら側で名前解決するかを選べます。以下の2つのコマンドを比較してください。1つ目はcurlがローカルで名前を解決し、2つ目はSOCKSプロキシに名前解決を任せます。
curl --noproxy "" --socks5 127.0.0.1:7890 \
--max-time 20 -I https://example.com/
curl --noproxy "" --socks5-hostname 127.0.0.1:7890 \
--max-time 20 -I https://example.com/
ローカルで名前解決する経路では失敗し、プロキシ側での名前解決なら成功する場合は、システムのリゾルバーを優先して確認します。ただし、これは手がかりであって確定ではありません。2回の接続で異なるIPアドレスやルールが使われる可能性があるためです。続けて、システムが実際に使っている名前解決設定を確認してください。
getent ahosts example.com
ls -l /etc/resolv.conf
cat /etc/resolv.conf
# systemd-resolvedが有効なシステムでのみ使用してください
resolvectl status
resolvectl query example.com
/etc/resolv.confがsystemd-resolvedやNetworkManagerの管理するファイルを参照している場合、直接上書きしても再接続後に元へ戻ることがあります。DHCPによる更新と手動編集を併用せず、該当するネットワーク管理ツールでDNSを変更してください。
mihomoのDNS待ち受けとfake-ipの経路を確認する
実際に使用している設定にdns.listen: 127.0.0.1:1053が設定されている場合は、digをインストールした端末からコアのDNSに直接問い合わせできます。待ち受けポートが異なる場合は、コマンドも合わせて変更してください。
dig @127.0.0.1 -p 1053 example.com A
dig @127.0.0.1 -p 1053 example.com AAAA
enhanced-mode: fake-ipでは、ドメイン名に対して設定範囲内の合成アドレスが返されることがあります。よく使われる範囲は198.18.0.1/16です。これはインターネット上の接続先アドレスではありません。後続の接続は、ドメイン名との対応を保持しているプロキシコアに戻す必要があります。アプリがfake-ipを取得した後、プロキシを経由しないルートで直接接続すると、タイムアウトが続くことがあります。
上流DNS自体の名前解決やプロキシ経路が必要になる場合もあります。default-nameserver、nameserver、proxy-server-nameserverの用途と現在の設定を確認し、ノードのドメイン名を解決するために、まだ確立していないプロキシ経路へ依存しないようにしてください。各項目の説明は設定リファレンスを参照してください。上流DNSをすべて同時に変更してから、どれが効いたのか推測するのは避けましょう。
5. ルールの適用先とノードの状態を確認する。遅延の数値だけで判断しない
クライアントの接続ログでテスト対象のドメインを探し、最終的な送信先がDIRECT、特定のプロキシノード、拒否のいずれかを確認します。ルールは上から順に照合され、末尾のMATCHが最後の受け皿です。それより前のドメインルールやIPルールが適用済みなら、フォールバックグループを変更してもそのリクエストには反映されません。
- テスト時刻と接続先ドメインを記録し、明示的なプロキシ指定でcurlをもう一度実行します。
- 接続ログまたはログファイルで該当ドメインを探し、適用されたルールとポリシーグループを確認します。
- ポリシーグループの現在の選択内容を確認します。特に、入れ子になったグループの最終的な接続先ノードを確認してください。
- 動作確認済みの別ノードを一時的に選び、接続先、リクエスト方法、モードを変えずに比較します。
遅延テストに成功しても、その時点で設定されたテストURLに到達できたことしか分かりません。すべてのサイトで使えるとは限らず、ノードによっては特定の接続先、プロトコル、アドレスファミリーでのみ失敗することがあります。サブスクリプションの更新成功も、設定を取得できたことを示すだけです。ノードの認証、利用量、サーバーポートが正常であることまでは証明しません。
ログレベルを一時的に上げて、確認後に戻す
必要に応じて実際に使っている設定のlog-levelを一時的にdebugにし、問題を一度再現したらinfoに戻します。GUIクライアントでは、クライアントが用意する設定オーバーライドまたはログ設定を使い、次回の更新で生成済み設定が上書きされないようにしてください。ログを共有する前に、サブスクリプションURL、認証情報、非公開のアクセス先ドメインを削除します。
# 独自のユーザーサービス:設定を変更したら、まずテストし、成功した場合だけ再起動します
mihomo -t -d "$HOME/.config/mihomo" && \
systemctl --user restart mihomo.service
journalctl --user -u mihomo.service --since "5 minutes ago" --no-pager
証明書エラーが発生したら、まずtimedatectl statusを実行して、時刻と時刻同期の状態を確認します。続けて証明書ストアとノードのドメイン名を確認してください。証明書検証の無効化を通常の修正方法にしないでください。接続先の誤りや通信経路の問題を見逃すおそれがあります。
6. 明示的なプロキシでは成功し、TUNで失敗する場合:権限、ルート、IPv6を確認する
curl -xでは正常なのにTUNを有効にするとブラウザーで失敗する場合、ポートとノードが使えることは確認できています。重点を透過的な通信の取り込みに切り替えましょう。TUNには仮想インターフェース、適切な権限、ルーティングが必要です。設定のtun.enable: trueだけでは、必要な条件がそろっているとは限りません。
ls -l /dev/net/tun
ip -br link
ip rule show
ip route show table all
ip -6 route show table all
/dev/net/tunが見つからない:システムがTUNに対応し、有効になっているか確認してください。コンテナーや制限付きの仮想化環境では、ホスト側の許可も必要です。- インターフェースの作成またはルート変更に失敗したというログがある:クライアントが提供するサービスモードを使うか、独自サービスの権限を確認してください。実行ファイルの権限を安易に広げないでください。
- 別のVPNも同時に動作している:両方のポリシールーティング、デフォルトルート、DNSの取り込みを比較します。確認時は通信を取り込む経路を一つだけ有効にしてください。
- IPv4は正常でIPv6だけ失敗する:IPv6のデフォルトルート、DNSのAAAA応答、使用中のコアのIPv6処理設定をそれぞれ確認してください。
# TUNを無効にした状態で、ホストのIPv4 / IPv6の直接接続を比較します
curl --proxy "" --noproxy "*" -4 --max-time 15 -I https://example.com/
curl --proxy "" --noproxy "*" -6 --max-time 15 -I https://example.com/
ここでの-4と-6は、直接接続の基準を確認するためのオプションです。ローカルHTTPプロキシに同じオプションを付けても、リモートノードが接続先に使うアドレスファミリーを確実に指定できるわけではありません。ネットワーク自体にIPv6の出口がない場合、IPv6のテストが失敗してもノードの故障とは限りません。
7. 変更は必要最小限にして復旧し、再現時の記録を残す
調査が終わったら、同じ接続先で端末、ブラウザー、TUNの3つの経路を再テストします。トレイアイコンや一度の速度測定だけで復旧したと判断しないでください。再発時にすぐ比較できるよう、次の情報を記録しておくと便利です。
- 実行中のインスタンス:コアのバージョン、実際の設定ファイルのパス、待ち受けアドレス、mixed-portを記録します。バージョンは
mihomo -vで確認できます。 - アプリの接続経路:ブラウザーがシステムプロキシと手動プロキシのどちらを使っているか、端末にプロキシ環境変数が設定されているかを記録します。
- 名前解決の経路:システムDNS、コアのDNS待ち受けポート、fake-ipの使用有無、ブラウザーのセキュアDNS設定を記録します。
- 送信結果:テスト対象のドメインに適用されたルール、実際のノード、エラーの発生時刻を記録します。サブスクリプションキーは記録しないでください。
- 設定の復元:debugログを通常のレベルに戻し、一時的なグローバルモードや不要になったテスト設定を元に戻します。
切り分けの要点は、明示的なプロキシで失敗するならコアと送信先、明示的なプロキシでは成功してブラウザーで失敗するならアプリの接続設定、TUNだけ失敗するなら透過的な通信の取り込みを確認することです。ローカルでの名前解決だけ失敗する場合は、システムDNSの経路を調べます。基本設定を作り直す場合は、クイックスタートに沿って最小限の接続を確立してから、ルール、Provider、TUNを一つずつ戻してください。