まず障害がどの層で起きているかを判断する
ブラウザーではWebページを開けるのに、ターミナルの curl、git、パッケージマネージャー、開発ツールで接続に失敗する場合、Clashのコアが停止しているとは限りません。よくあるのは、両者が異なるプロキシ経路を使っているケースです。ブラウザーはOSのプロキシ設定を参照することもあれば、拡張機能が個別に制御することもあります。一方、ターミナルのプログラムは独自設定、プロキシ環境変数、コマンドライン引数だけを参照することが多くあります。
切り分けでは、経路を4層に分けます。クライアントの画面がコアを正常に管理できているか、コアがローカルポートを待ち受けているか、対象プログラムがそのポートへリクエストを送っているか、そしてコアに入った後のDNSとルール処理が正しいかを確認します。1回に1層だけ検証することで、障害の境界を特定できます。
| 症状 | 優先して確認する項目 | よくある原因 |
|---|---|---|
| ブラウザーは正常、curlは失敗 | ターミナルのプロキシ環境変数 | curlがシステムプロキシを参照していない、または変数が古いポートを指している |
| ブラウザーもターミナルも失敗 | コアの状態と待受ポート | コアが起動していない、設定の読み込みに失敗している、またはポートが使用中 |
| ドメイン名は失敗、IPリクエストは成功 | DNS経路 | ローカルの名前解決に失敗している、アプリがClashのDNSを回避している、またはfake-ipに非対応 |
| Gitまたはパッケージマネージャーだけ失敗 | アプリ専用の設定 | アプリが環境変数を上書きしている、または古いプロキシアドレスを保持している |
| TUNを有効にするとターミナルが復旧 | 従来のシステムプロキシ経路 | プログラムがもともとシステムプロキシを参照せず、TUNによってネットワーク層から制御されるようになった |
手順1:コアが起動し、正しいポートを待ち受けているか確認する
クライアント画面で実際のポートを確認する
ポートが必ず 7890 だと決めつけないでください。Clashやmihomoの設定では mixed-port: 7890 がよく使われますが、port: 7890 と socks-port: 7891 を分けて設定している場合もあります。また、ポート競合によってクライアントが別の番号へ変更することもあります。現在使用しているクライアントの「設定」→「パラメーター設定」または「設定」→「ポート設定」で、HTTP、SOCKS5、Mixed Portの実際の値を確認してください。メニュー名はクライアントのバージョンによって異なるため、画面の表示を優先します。
設定ファイルでよく見られる待受設定は次のとおりです。ここでは項目を見分けるために示しているだけで、実際の切り分けでは現在読み込まれている設定と稼働状態を基準にしてください。
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
mixed-port は、同じポートでHTTPとSOCKS5の接続を受け付けます。ポートを分けている場合、HTTPクライアントは port、SOCKS5クライアントは socks-port に接続します。プロトコルとポートを取り違えると、接続直後に切断される、空のレスポンスが返る、プロキシハンドシェイクに失敗するといった症状が出ます。
ローカルで待受状態を確認する
Windows PowerShellでは、指定したポートが待受状態かどうかを確認できます。
Get-NetTCPConnection -State Listen |
Where-Object LocalPort -In 7890,7891 |
Select-Object LocalAddress,LocalPort,OwningProcess
macOSとLinuxでは、次のコマンドを使用できます。
lsof -nP -iTCP:7890 -sTCP:LISTEN
lsof -nP -iTCP:7891 -sTCP:LISTEN
ss が利用できる場合は、次も実行できます。
ss -lntp | grep -E ':(7890|7891)\b'
127.0.0.1:7890 と表示される場合、そのポートはローカルからの接続だけを受け付けています。ターミナルとClashが同じマシン上にあるなら、これで十分です。WSL、コンテナ、仮想マシン、または同じLAN内の別の端末からコマンドを実行している場合、その環境の 127.0.0.1 はホストではなく自身を指します。この場合は、ホストのアドレス、仮想ネットワークの境界、allow-lan の設定を明確に確認してください。待受アドレスをいきなりネットワーク全体から到達可能にして、確認を終わらせないようにしましょう。
手順2:システム設定を迂回し、プロキシポートを直接テストする
ポートの待受を確認したら、明示的な引数でリクエストを直接プロキシへ送ります。これにより、「コアとノードが利用できるか」と「OSがプロキシ設定を正しく振り分けているか」を切り分けられます。
curlでHTTPプロキシを確認する
curl -v --connect-timeout 10 \
-x http://127.0.0.1:7890 \
https://example.com/
Windows PowerShellで curl が Invoke-WebRequest のエイリアスになっている場合は、curl.exe を明示的に呼び出します。
curl.exe -v --connect-timeout 10 ^
-x http://127.0.0.1:7890 ^
https://example.com/
明示的なプロキシリクエストは成功するのに、-x なしのリクエストが失敗する場合、コア、待受ポート、現在のポリシーはおおむね利用可能で、問題はターミナルのプログラムがプロキシを使っていないことに絞られます。明示的なリクエストも失敗する場合は、Clashのログも確認してください。ログに接続記録がまったくなければ、通常はリクエストがそのポートへ届いていません。接続記録はあるものの、ポリシーが REJECT になっている、ノードがタイムアウトする、TLSエラーが出る場合は、ルール、プロキシグループ、上流接続を引き続き確認します。
SOCKS5を確認し、ローカル名前解決とプロキシ側の名前解決を区別する
curl -v --connect-timeout 10 \
--proxy socks5h://127.0.0.1:7891 \
https://example.com/
socks5h の h は、ドメイン名の解決をプロキシ側で行うことを示します。一方、socks5 では通常、まずローカルでドメイン名を解決します。socks5h は成功するのに socks5 が失敗するなら、重点はプロキシポートからローカルDNSへ移ります。ノードをすぐに変更するより、この比較テストのほうが原因を明確にできます。
テスト先は、安定していて現在のルールの想定に合うアドレスを選びます。あるドメインが DIRECT に設定されていれば、選択したプロキシノードを経由しない可能性があります。REJECT なら、失敗は設定どおりの結果です。ルールモードでは上から順に評価され、最初に一致したルールが行き先を決めます。そのため、ログで実際に一致したルールとポリシーも確認してください。
手順3:ターミナルに正しいプロキシ変数を設定する
macOSとLinuxの現在のShell
HTTPまたはMixed Portを使うローカルのClashでは、現在のターミナルセッションに次のように設定できます。
export HTTP_PROXY=http://127.0.0.1:7890
export HTTPS_PROXY=http://127.0.0.1:7890
export ALL_PROXY=socks5h://127.0.0.1:7891
export NO_PROXY=localhost,127.0.0.1,::1
小文字の変数だけを認識するプログラムもあれば、大文字の変数を優先するプログラムもあります。互換性の違いを除外するには、同じ値で対になる変数を設定します。
export http_proxy="$HTTP_PROXY"
export https_proxy="$HTTPS_PROXY"
export all_proxy="$ALL_PROXY"
export no_proxy="$NO_PROXY"
これらのコマンドが影響するのは、現在のShellと、そこから起動する子プロセスだけです。すでに開いているエディター、ターミナルのタブ、バックグラウンドサービスは自動的に変更されません。永続化する場合は、使用しているShellに合わせて ~/.zshrc、~/.bashrc、または該当する起動ファイルへ記述し、変更後に新しいターミナルを開いて確認します。複数の起動ファイルに異なるポートを残さないでください。
Windows PowerShellの現在のセッション
$env:HTTP_PROXY = "http://127.0.0.1:7890"
$env:HTTPS_PROXY = "http://127.0.0.1:7890"
$env:ALL_PROXY = "socks5://127.0.0.1:7891"
$env:NO_PROXY = "localhost,127.0.0.1,::1"
$env: で設定した変数は、現在のPowerShellプロセスとその子プロセスでのみ有効です。システム設定からユーザー環境変数を書き込んでも、すでに起動しているターミナルには自動的に反映されません。いったん閉じて、再度開く必要があります。実際の値を確認するには、次を実行します。
Get-ChildItem Env: |
Where-Object Name -Match '^(HTTP|HTTPS|ALL|NO)_PROXY$'
古いポートと誤った変数を削除する
クライアントでポートを変更した後も、ターミナルに古い値が残っていることがあります。たとえばClashは 7897 に変更したのに、HTTPS_PROXY は 7890 を指したままというケースです。macOSとLinuxでは、次のコマンドで一時的に削除できます。
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
unset http_proxy https_proxy all_proxy no_proxy
PowerShellの現在のセッションでは、次を使用できます。
Remove-Item Env:HTTP_PROXY -ErrorAction SilentlyContinue
Remove-Item Env:HTTPS_PROXY -ErrorAction SilentlyContinue
Remove-Item Env:ALL_PROXY -ErrorAction SilentlyContinue
Remove-Item Env:NO_PROXY -ErrorAction SilentlyContinue
削除後、まずプロキシなしのリクエストを1回実行し、その後、実際のポートを使って設定し直します。これにより、複数の設定元が互いに上書きしていないか確認できます。
手順4:Git、パッケージマネージャー、開発ツールの独自設定を確認する
環境変数が正しくても、すべてのツールがそれを使うとは限りません。Git、npm、Pythonツールチェーン、Javaビルドツール、エディター、コンテナランタイムには、それぞれ独自のプロキシ設定が保存されている場合があります。切り分けでは、まず設定元を確認し、削除するか変更するかを決めます。すべての設定層に同時に値を書き込むのは避けてください。
Gitのグローバル設定とリポジトリ設定
git config --show-origin --get-regexp 'http\..*proxy|https\..*proxy'
--show-origin を使うと、設定元がシステム、ユーザー、現在のリポジトリのどれか表示されます。古いポートが見つかった場合は、グローバル設定を更新できます。
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890
Gitを環境変数に従わせる場合は、独自の上書き設定を削除します。
git config --global --unset http.proxy
git config --global --unset https.proxy
リポジトリ単位の設定も確認してください。.git/config の設定がユーザー設定を上書きすることがあります。HTTPSリクエストをHTTPプロキシ経由で送る場合、https.proxy の値は通常 http:// で始まります。これはクライアントがまずHTTPプロキシへ接続し、CONNECTでTLSトンネルを確立することを示すもので、最終的なWebサイトが平文HTTPを使うという意味ではありません。
npm、pnpm、Pythonツール
npm config get proxy
npm config get https-proxy
pnpm config get proxy
pnpm config get https-proxy
python -m pip config list -v
出力が古いアドレスを示す場合は、該当ツールのユーザー設定を修正します。npmでは次のコマンドで上書き設定を削除し、環境変数を再び参照させられます。
npm config delete proxy
npm config delete https-proxy
Pythonのpipは、環境変数だけでなく、ユーザーディレクトリや仮想環境内の設定を読み込むこともあります。単独の pip ではなく python -m pip を使うと、現在のPythonインタープリターに対応するツールを確認できます。
エディター内蔵ターミナルとリモート開発
デスクトップのアイコンから起動したエディターは、ターミナルのプロキシ変数を変更する前から起動している可能性があります。内蔵ターミナルが引き継ぐのは、エディタープロセスの起動時点の環境です。そのため、エディターを完全に終了してから再起動してください。リモートSSH、開発コンテナ、WSLはそれぞれ独立した環境です。ホストのシステムプロキシがリモートホストのプロキシになることはなく、ホストの 127.0.0.1:7890 にリモートホストから接続することもできません。
手順5:DNS、ルールモード、TUNの違いを切り分ける
ドメイン名とIPを比較してDNS経路を確認する
ターミナルのエラーに Could not resolve host、Name or service not known、getaddrinfo failed が含まれている場合、接続はまだ対象サイトに到達していません。まずシステムの名前解決をテストします。
nslookup example.com
curl -v https://example.com/
続いて、socks5h を使ったリクエストと比較します。プロキシ側の名前解決は成功するのに通常のリクエストが失敗する場合は、OSのDNS、VPNやネットワーク拡張の名前解決経路、アプリがDNSを独自指定していないかを確認します。mihomoの fake-ip モードは予約アドレスを返し、コアがドメイン名との対応付けを行います。そのため、対象の通信が実際にコアへ戻る必要があります。システムのネットワークスタックを迂回したり、カスタムDNSを固定的に使用したりするプログラムでは、異なる結果になる場合があります。
no-resolve は、このパラメーターを付けたIP系ルールにだけ作用します。該当するIPルールに一致させるためにドメイン名を積極的に解決しないという意味であり、Clashやmihomo全体のDNSを無効にするものではありません。設定に IP-CIDR,...,no-resolve があっても、すべてのドメイン名前解決の障害をこのパラメーターのせいにしないでください。
モードの切り替えよりルールログのほうが多くの情報を得られる
Ruleモードでは、コアが設定内のルールを上から順にリクエストへ適用します。ログには通常、対象のドメイン名またはIP、適用されたルール、使用されたポリシーグループ、最終的な出口が表示されます。ブラウザーとターミナルが同じドメインへ接続しているのに異なるルールへ振り分けられる場合、ターミナルが先にドメインをIPへ解決している、アプリが別のサブドメインへ接続している、IPv4とIPv6で結果が異なる、または両者が同じコアポートに入っていないことがよくあります。
モードを一時的にGlobalへ切り替えるのは、原因の範囲を絞るためだけに使います。Globalで成功してRuleで失敗するなら、ルールとポリシーグループを確認します。両方で失敗するなら、ポート、DNS、ノード、ネットワーク経路を引き続き確認してください。テスト後は元のモードに戻し、Globalを常用してルール問題の解決策にしないでください。
TUNで解決できること、代替できないこと
システムプロキシは、OSのプロキシ設定を参照するアプリを主な対象とします。TUNモードは仮想ネットワークインターフェースを通じて、より広範囲のIP通信を引き受けます。そのため、HTTPやSOCKS5プロキシに対応していない一部のターミナルプログラムが、TUNを有効にすると接続できるようになる場合があります。これは、元のアプリケーション層のプロキシ経路がそのプログラムを対象にしていなかったことを示すもので、システムプロキシのボタン自体が壊れているわけではありません。
TUNを有効にする前に、クライアントの「設定」→「TUNモード」または該当するネットワーク設定で、必要な権限を確認してください。Windowsではサービスモードや管理者権限が必要になることがあります。macOSではVPN構成やネットワーク拡張の許可が求められる場合があります。Linuxでは通常、ネットワーク管理権限、ルーティング、DNS設定が関係します。mihomoを基盤とするクライアントでも実装や入口は異なるため、現在の画面とログを基準にしてください。
スイッチを繰り返し切り替えるのではなく、結果から原因を絞り込む
ここまでの確認を終えたら、次の順序で再現可能な結論をまとめられます。各手順のコマンド、ポート、ログの時刻を記録しておけば、クライアントや設定を変更した後もすぐに再テストできます。
- クライアントで現在のコアが稼働中であることを確認し、HTTP、SOCKS5、Mixed Portの実際のポートを記録します。
lsof、ss、Get-NetTCPConnectionを使い、そのポートを想定したプロセスが実際に待ち受けていることを確認します。curl -xまたはcurl --proxy socks5h://で明示的にプロキシへ接続し、Clashのログに該当するリクエストが現れるか確認します。- 明示的なプロキシ接続が成功したら、
HTTP_PROXY、HTTPS_PROXY、ALL_PROXYと大文字・小文字の変数を確認します。 - 特定のツールだけが失敗する場合は、Git、npm、pip、エディター、ビルドツール自身の設定元を確認します。
- ドメイン名だけ失敗し、プロキシ側の名前解決が成功する場合は、システムDNS、fake-ipからの通信の戻り、アプリ独自のDNSを確認します。
- TUNでのみ成功する場合は、対象プログラムがもともとシステムプロキシに対応していたかを確認し、ターミナル、コンテナ、リモート環境のネットワーク境界も調べます。
典型的な結果として、ClashのMixed Portが実際には 7897 で、ブラウザーはクライアントが書き込んだばかりのシステムプロキシを参照するため正常に接続できる一方、ターミナルの HTTPS_PROXY は 127.0.0.1:7890 を指したままで、Gitのユーザー設定にも同じ古いポートが保存されていることがあります。修正は、まずターミナルの変数を 7897 に変更し、次にGitの上書き設定を削除または更新し、最後に明示的なプロキシ接続と通常のGitリクエストを別々に再テストします。この過程でRule、Global、TUNを何度も切り替える必要はありません。
別のよくある結果は、HTTPプロキシのテストには失敗するものの、socks5h://127.0.0.1:7891 では成功するケースです。まずHTTPポートが存在するか確認し、SOCKS5ポートをHTTPポートとして使わないでください。通常のSOCKS5は失敗するのに socks5h が成功するなら、次にローカルDNSを確認します。このように層ごとに比較すれば、「Clashが効かない」という問題を、ポート、アプリ設定、名前解決、ルールのいずれかへ絞り込めます。