1. まずエラー文言で分類し、確認すべき層を決める

証明書エラーはブラウザが TLS ハンドシェイクの結果を言い換えたもので、Clash の出力ではありません。Chrome、Edge、Firefox は失敗した段階をエラー文言に含めています。まずこの一文をそのまま控えてから、下の表と照らして確認すべき層を決めるほうが、プロキシを何度もオンオフするよりはるかに効率的です。

Chrome / Edge の証明書エラー文言と優先確認ポイント
エラー文言意味優先して確認する項目
NET::ERR_CERT_DATE_INVALID証明書が有効期間内にないシステム時刻、ルート証明書の有効期限
NET::ERR_CERT_AUTHORITY_INVALID発行者が本機の信頼リストにない中間証明書の欠落、ローカルの傍受ソフト
NET::ERR_CERT_COMMON_NAME_INVALID証明書のドメインとアクセス先ドメインが一致しないDNS 解決結果、hosts のマッピング
NET::ERR_CERT_REVOKED証明書が失効している対象サイト側の問題
SSL_ERROR_BAD_CERT_DOMAIN(Firefox)ドメイン不一致。上記と同じDNS 解決と SNI

Firefox は独自の NSS 証明書ストアを使い、システムのルート証明書ストアを読みません。企業ゲートウェイやパケットキャプチャツールのルート証明書をシステムにインストールしている場合、Chrome では正常に開けても Firefox では SEC_ERROR_UNKNOWN_ISSUER が出ることがあります。「同じドメインなのにブラウザによって挙動が違う」ときは、プロキシ経路ではなく証明書ストアを先に疑ってください。

コマンドラインのほうが詳細を素早く取得できます。使うコマンドは2つだけです。

curl -vI https://example.com --max-time 8
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null

curl が SSL certificate problem: unable to get local issuer certificate を返す場合は、証明書チェーンが不完全か、対応するルート証明書が本機にないことを意味します。certificate has expired を返す場合は時刻か有効期限の問題です。この2つの情報は、ブラウザのダイアログより根本原因に近いところにあります。

まず1つの操作で切り分ける

クライアントを終了し、システムプロキシをオフにして、同じブラウザで同じドメインにアクセスします。プロキシを切って正常に戻れば、問題はプロキシ経路かノード側です。切っても依然としてエラーが出るなら、問題は本機の証明書ストアかサイト自体にあり、Clash とは関係ありません。ここまで済ませておけば、以降の作業が無駄になりません。

2. ステップ1:システム時刻と証明書の有効期限を確認する

証明書の有効期間は notBefore と notAfter の2つの時点で区切られ、システム時刻がこの範囲外だとハンドシェイクは失敗します。ノート PC の長期スリープ、デュアルブートの切り替え、仮想マシンのスナップショット巻き戻しなどで、システムクロックは数分から数日ずれますが、ユーザーが気づくことはほとんどありません。

  • Windows:設定 → 時刻と言語 → 日付と時刻 → 「時刻を自動的に設定する」をオンにし、「今すぐ同期」をクリックします。コマンドラインでは w32tm /resync で強制同期、w32tm /query /status で現在のタイムソースを確認できます。
  • macOS:システム設定 → 一般 → 日付と時刻 → 「日付と時刻を自動的に設定」をオンにします。ターミナルで sudo sntp -sS time.apple.com を実行すれば手動で時刻を合わせられます。
  • Linux:timedatectl status で NTP 同期状態を確認し、sudo systemctl restart systemd-timesyncd で時刻同期サービスを再起動します。

時刻が正しいのに ERR_CERT_DATE_INVALID が出る場合は、ルート証明書自体を確認します。DST Root CA X3 は 2021-09-30 に失効し、当時多くの古い Android 端末や旧版 OpenSSL クライアントで連鎖的なエラーが発生しました。原因は、サーバーが今もこのルートを指すクロス署名チェーンを配信していたことです。本機のルート証明書一覧を確認する入口:Windows は certlm.msc を実行 → 信頼されたルート証明機関 → 証明書、macOS は「キーチェーンアクセス」→ システム → 証明書、Linux は /etc/ssl/certs ディレクトリを確認します。

判定は簡単です。同じドメインを時刻が正常な別の端末で開いて問題なければ、原因は本機の時刻か証明書ストアにあり、ノードとは無関係なので、それ以上調べる必要はありません。

3. ステップ2:ルート証明書チェーンとローカルの傍受ソフトを確認する

中間証明書の欠落はサーバー側の設定問題ですが、症状はクライアント側の問題に見えがちです。サーバーがサイト証明書だけを送り、中間証明書を送らない場合、ブラウザはキャッシュされた中間証明書や証明書内の AIA 拡張で補おうとします。ブラウザを変えたりキャッシュを消したりした後に補完に失敗すると、ERR_CERT_AUTHORITY_INVALID が表示されます。

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | grep -c "BEGIN CERTIFICATE"

出力が 1 なら、サーバーが返したのはサイト証明書だけで、チェーンが不完全です。正常なら 2〜3 が返ります。この種の問題はサイト側が中間証明書を追加で送ることでしか解決できず、クライアント側の設定変更に意味はありません。

ローカルの傍受系ソフトは2つ目の頻出要因です。企業ゲートウェイ、Web 保護機能付きのセキュリティソフト、パケットキャプチャツール(Charles、Fiddler、mitmproxy)は、本機に自己署名のルート証明書をインストールし、アクセス先のすべてのサイト証明書をそれで再署名します。このルート証明書がシステムストアに入っていれば Chrome と Edge は信頼し、すべて正常に開けます。一方、Firefox、Java アプリ、一部の Electron クライアントは独自の証明書ストアを使うためエラーになります。確認すべき箇所は次のとおりです。

  • Windows:certlm.msc → 信頼されたルート証明機関 → 証明書。「発行者」列で並べ替え、公共の CA ではない項目(企業ドメイン、セキュリティソフトのベンダー名)を探します。
  • macOS:キーチェーンアクセス → システム → 証明書で、出所不明の自己署名ルート証明書がないか確認します。
  • Firefox:設定 → プライバシーとセキュリティ → 証明書 → 証明書を表示 → 認証局で、システムの一覧と1件ずつ照合します。

検証をスキップしてエラーを回避しない

curl の --insecure や、ブラウザの ignore-certificate-errors 起動オプションは、検出機能そのものをオフにする行為です。問題は目に見えるエラーから見えないリスクに変わり、調査の手がかりも一緒に消えてしまいます。

ここは明確にしておきます。Clash と mihomo コアは TCP/UDP の転送とルールによる振り分けだけを行い、TLS ハンドシェイクには関与せず、HTTPS トラフィックも復号しません。プロキシ経路自体が証明書を差し替えることはありません。MITM 系のツールを別途挟んでいない限り、証明書エラーの原因をコアに求めるのは、最初から方向が間違っています。

4. ステップ3:プロキシ経路、DNS、振り分け設定

ドメインが誤った IP に解決されることは、証明書エラーの3つ目の要因です。証明書の CN と SAN はドメインに紐づいているため、そのドメインに属さない証明書を受け取ると、ブラウザは ERR_CERT_COMMON_NAME_INVALID を表示します。よくあるのは次の3つの場面です。DNS が汚染されて誤ったアドレスを返した、hosts に手書きしたマッピングが古くなっていた、fake-ip アドレスを実アドレスと誤認して別のツールに入力した。

dns:
  enable: true
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  nameserver:
    - https://223.5.5.5/dns-query

fake-ip モードでは、mihomo はドメインに 198.18.0.1/16 の範囲の仮想アドレスを割り当てます。このマッピングはクライアント内部にしか存在せず、接続確立時にドメインへ戻されるため、TLS 検証には関与しません。本当に問題になるのは、198.18.x.x を hosts やプロキシを通さないツールに書き込んだ場合です。ドメイン情報が失われると、証明書が一致しなくなるのは当然です。

振り分けルールも間接的に証明書エラーを引き起こします。あるドメインがルールで直結と判定され、現在のネットワークがそのドメインをハイジャックしている場合、返ってくるのは偽の証明書です。確認方法:Clash Verge の「接続」ページでこの接続を探し、一致したルールと出口ノードを確認してから、そのドメインをプロキシノードに切り替えて再試行します。切り替え後に正常に戻れば、問題は直結の出口側にあり、証明書側ではありません。

システムプロキシが適用されていないケースは別に切り分けます。一部のアプリはシステムプロキシ設定を読まず、通信が直結になり、遮断されたときに出るのは接続リセットやタイムアウトであって証明書エラーではありません。この2種類のエラーをまとめて調べると遠回りになります。ポートが一致しているか確認してください。mixed-port は 7890 が一般的で、Clash Verge 系の既定は 7897 です。ブラウザ拡張とシステムプロキシは同じポートを指す必要があります。

TUN モードはネットワークアダプタ層で通信を引き継ぐため、アプリがプロキシに対応しているかどうかに依存しません。ただし TLS を復号するわけではなく、証明書の検証は依然としてブラウザ側で行われます。TUN モードで証明書エラーが出たら、TUN 自体を何度もオンオフするのではなく、DNS と振り分けルールを先に確認してください。

5. 順番に実行する確認チェックリスト

  1. エラー文言全体、ドメイン、発生時刻を記録し、継続的なエラーか断続的かを確認する。
  2. クライアントを終了してシステムプロキシをオフにし、同じブラウザで再試行してプロキシが関係しているかを判断する。
  3. システム時刻を合わせ、ブラウザを再起動してもう一度アクセスする。
  4. openssl s_client -connect ドメイン:443 -servername ドメイン -showcerts を実行し、証明書チェーンが完全で、有効期限が現在時刻をカバーしているか確認する。
  5. システムのルート証明書一覧を確認し、企業ゲートウェイやセキュリティソフトがインストールした傍受証明書を除外する。Firefox は別途照合が必要。
  6. クライアントの「接続」ページで、そのドメインが一致したルール、出口ノード、DNS 解決結果を確認する。
  7. そのドメインを別のノードまたは別のポリシーグループに切り替え、エラー文言が変わるか観察する。
  8. 別のネットワーク(スマートフォンのテザリングなど)で相互検証し、ローカルネットワークの問題かノード側の問題かを切り分ける。

チェックリストの中では手順2と手順4が最も費用対効果が高いです。一方は責任範囲を切り分け、もう一方は証明書チェーンの実際の状態を直接示してくれます。多くのケースはここまでで特定できます。

6. よくある誤解

  • ノードを変えれば証明書エラーが解決する。ノードはバイトストリームを転送するだけで、証明書は接続先サイトが提示します。ノードを変えても同じエラーが出るなら、ノードはほぼ除外できます。
  • TUN モードは証明書検証を変える。変わりません。TUN が変えるのは通信の引き継ぎ方であり、TLS 検証は依然としてブラウザ側で行われます。
  • サブスクリプションを更新すれば証明書エラーが直る。サブスクリプションの内容はノード一覧とルールにしか影響せず、システムの証明書ストアには書き込みません。
  • ブラウザのキャッシュを消せば解決する。中間証明書が欠落していて、以前キャッシュで補完していた場合にたまたま効くだけで、安定した解決策ではありません。
  • 別のクライアントに変えれば直る。主要なクライアントは mihomo コアと同一のシステムプロキシ設定を共有しており、クライアントを変えても TLS 検証の経路は変わりません。

順番を固定しましょう。エラー文言 → システム時刻 → ルート証明書チェーン → プロキシと DNS → ノード。最初の2ステップでほとんどのケースをカバーできます。コアを第一容疑者にすると、たいてい時間を余計に使うだけです。