TLSハンドシェイク失敗・証明書エラーの対処法:システム時刻、SNI、allowInsecureの3つの確認ポイント

証明書エラーが出たときは、ノードを何度も切り替えるだけでは原因を特定できません。まずシステム時刻、次にSNIと接続先アドレスの関係、最後にサーバー証明書チェーンを確認すれば、クライアント設定とサーバー側のどちらに問題があるかをすばやく切り分けられます。

この記事の要点

certificate、x509、handshake、unknown authorityなどのログが出るv2rayN、v2rayNG、v2flyNGユーザー向けの記事です。元のログを保存し、システム時刻とタイムゾーン、SNI、証明書の有効期限とチェーンを順に確認します。allowInsecureは短時間の切り分けに限って使い、恒久的な解決策にはしないでください。

TLSエラーが発生する段階

VMessまたはVLESSノードでTLSを有効にしても、クライアントはいきなりプロキシデータを送信するわけではありません。まずTCP接続を確立し、SNI、対応TLSバージョン、ALPNなどを含むTLS ClientHelloを送信します。サーバーが証明書チェーンを返してセッション鍵をネゴシエートし、ハンドシェイクに成功して初めて、プロトコル認証とデータ転送が暗号化された通信路に入ります。

ノードアドレスを名前解決TCP接続を確立SNIを送信証明書を検証プロキシプロトコルへ移行

そのため、「ノードに接続できない」からといって、アカウントが無効になったとは限りません。ログに証明書の期限切れ、ホスト名の不一致、未知の認証局が表示されている場合、問題は通常、VMessのユーザーID、VLESSのUUID、転送経路、ルーティング規則より前の段階にあります。この場合、サブスクリプションのグループ、システムプロキシのポート、分割ルールを変更しても、証明書エラーは解消しません。

切り分けでは、ブラウザーの表示だけで判断せず、まずクライアントのコアログを開きます。v2rayNではメイン画面下部のログ欄で最近の接続を確認でき、「設定」→「パラメータ設定」でログレベルが低すぎないか確認します。v2rayNGとv2flyNGでは、対象設定の実行ログ画面を開き、最初に出たTLSまたはx509エラーを原文のまま記録してください。

エラー: x509: certificate has expired or is not yet valid

原因と対処:端末の時刻が証明書の有効期間から外れているか、サーバー証明書が実際に期限切れです。まずシステム時刻を同期し、ログに記録された現在時刻と証明書の有効期間を確認します。

エラー: x509: certificate is valid for another.example, not node.example

原因と対処:サーバーが返した証明書名と、クライアントが検証する名前が一致していません。ノードのSNIを確認し、サブスクリプションのメモ、WebSocketパス、IPアドレスをこの項目に入力しないでください。

エラー: x509: certificate signed by unknown authority

原因と対処:証明書チェーンに中間証明書がないか、証明書がシステムから信頼されていません。システムの証明書環境を更新し、複数の端末で同時に再現する場合は、サーバー側で証明書チェーンを完全に構成します。

エラー: remote error: tls: handshake failure

原因と対処:サーバーがハンドシェイクを拒否しています。SNI、TLSバージョン、ALPNの不一致がよくある原因です。まずサブスクリプションの元パラメータを確認し、次にサーバーのリスニング設定を調べます。

ステップ1:システム時刻とタイムゾーンを確認する

TLS証明書にはNot BeforeとNot Afterという2つの時刻境界があります。クライアントは端末の現在時刻を使って、証明書が有効か、期限切れでないかを判定します。システム時刻が数か月進んでいると正常な証明書を期限切れと判定し、数か月遅れていると有効期間前と表示されることがあります。タイムゾーン自体は絶対時刻を変えませんが、誤ったタイムゾーンと手動設定の時計が組み合わさると、実際の時刻にずれが生じます。

±60秒
推奨する時計の誤差範囲
443
TLSでよく使われるサーバーポート
TLS 1.2+
一般的な互換範囲
10808
v2rayNでよく使われるローカルSOCKSポート

Windowsでは「日付と時刻」の設定で時刻とタイムゾーンの自動設定を有効にし、「今すぐ同期」を一度実行します。Linuxのデスクトップではシステム時刻の状態を確認し、NTPサービスが同期中であることを確認します。Android端末では、ネットワーク提供の日時とタイムゾーンを有効にしてください。修正後はクライアントのコアを完全に停止して再起動し、失敗状態を引き継いだ古い接続を使わないようにします。

  1. 元のログを保存する

    現在のノードを切断し、ログを消去してからもう一度接続します。最初に出たx509またはTLSエラーとタイムスタンプを保存し、後続のタイムアウト情報だけを残さないでください。

  2. 現在時刻を確認する

    端末の日付、分、タイムゾーンを信頼できる時刻ソースと照合します。誤差はできるだけ60秒以内に抑え、手動時刻になっていないかも確認してください。

  3. 時刻を同期する

    システムの自動同期を有効にします。Linuxではtimedatectl statusを実行し、出力でシステムクロックが同期済みであることと、タイムゾーンが正しいことを確認します。

  4. クライアントのコアを再起動する

    v2rayNではサービスをいったん停止してから再起動します。Androidクライアントでは現在の設定を切断し、接続を終了してから再接続して、古いセッションを使わないようにします。

  5. 再テスト結果を比較する

    エラーからnot yet validの表示が消えた場合、原因は端末の時刻です。同じ証明書の有効期限が表示され続ける場合は、証明書が本当に期限切れかどうかを確認します。

ステップ2:SNIとノードアドレスを確認する

SNIはTLS ClientHelloで送信されるサーバー名です。1つのIPアドレスで複数のサイトを運用している場合、サーバーはSNIに応じて証明書と仮想ホストを選択します。ノードの接続先はドメイン名でもIPアドレスでも構いませんが、証明書の検証名は通常、証明書に記載されたドメイン名でなければなりません。メモから推測してはいけません。

たとえば、クライアントの接続先が入口IPで、証明書があるドメイン名に発行されている場合、ノード設定のserverNameまたはSNIにはそのドメイン名を入力します。逆に、ノードアドレス自体が正しいドメイン名で、サブスクリプションにSNIが明記されている場合は、指定値をそのまま使用します。WebSocketのHost、HTTPパス、サブスクリプション名、ノードのメモをSNIにコピーすると、名前が一致しなくなります。

設定項目 実際の役割 よくある誤り
アドレス DNSで名前解決し、TCP接続を確立するために使用 プロトコルの接頭辞やパスを含む完全なURLを入力する
ポート サーバー側のTLSリスニングポートを指定 ローカルの10808をリモートポートとして入力する
SNI / serverName サーバー証明書を選択し、検証名を決める IPアドレス、ノードのメモ、転送パスを入力する
Host HTTPまたはWebSocketのリクエストヘッダーに使用 Hostは必ずSNIと同じだと考える
Path WebSocketまたはHTTPの転送パスを指定 証明書名の不一致をパスの誤りで説明する

v2rayNでは対象ノードをダブルクリックするか、右クリックしてサーバーの編集を選び、TLS関連の項目でserverName、SNI、「偽装ドメイン名」を確認します。表示名はコアや設定の種類によって異なります。変更前にサブスクリプションの元の値を保存し、変更後はこの項目だけをテストしてください。ポート、転送方式、UUIDを同時に変更してはいけません。

v2rayNGまたはv2flyNGでは、まず接続を切断し、設定の右側にある編集ボタンをタップして、TLSセキュリティ設定内のサーバー名または偽装ドメイン名を探します。サブスクリプションから生成された設定は、次回の更新で手動変更が上書きされることがあります。正しい値が確認できたら、サブスクリプション提供元に元設定の修正を依頼してください。

エラー: certificate is valid for example.net, not 203.0.113.10

原因と対処:クライアントがIPアドレスで証明書を検証しています。サブスクリプションに明記された証明書のドメイン名をSNIに入力し、元の接続先アドレスは維持してください。検証を無効にして問題を隠してはいけません。

エラー: tls: unrecognized name

原因と対処:サーバーが現在のSNIを受け付けていません。サブスクリプションの元のserverNameに戻し、複数のクライアントで失敗する場合は、サーバーの仮想ホストとTLSリスニング設定を確認します。

ステップ3:証明書の期限切れとチェーン不足を確認する

システム時刻とSNIが正しいことを確認したら、サーバーが実際に返している証明書を調べます。証明書の期限切れはNot Afterが現在時刻より前になっている状態です。証明書チェーンの不足は、サーバーがサイト証明書だけを送信し、必要な中間証明書を送っていない場合によく発生します。どちらもサーバー側の設定問題であり、クライアントにサブスクリプションを再インポートしても直りません。

OpenSSLをインストールしたLinux環境では、次のコマンドで443番ポートから返される証明書チェーンを確認できます。-connectの後には実際の接続先ドメインとポートを、-servernameの後にはノードが要求するSNIを指定します。2つのパラメータは必ず併用してください。片方を省くと、別のデフォルト証明書を取得する可能性があります。

openssl s_client -connect server.example:443 \
  -servername node.example \
  -showcerts

openssl s_client -connect server.example:443 \
  -servername node.example \
  -verify_return_error < /dev/null

出力のsubjectは現在の証明書の主体、issuerは発行者、notBeforeとnotAfterは有効期間を示します。最終行にVerify return code: 0 (ok)と表示されれば、現在のシステムの信頼環境で完全な検証経路を構築できています。コード10は通常、証明書の期限切れを示します。コード20または21は、ローカルの発行者を取得できないか、最初の証明書を検証できない場合によく見られます。

allowInsecureで解決できることと、そのリスク

allowInsecureは、通常の検証に通らないサーバー証明書をクライアントが受け入れるための設定です。期限切れ、自署名、名前不一致、信頼されていない証明書を一時的に通過させられますが、サーバー証明書を修正したり、SNI、アドレス、ポート、転送設定を自動修正したりするものではありません。

有効にしてもTLS通信自体は暗号化されますが、クライアントはサーバーの身元を確実に確認する重要な手順を失います。攻撃者がネットワーク接続に介入できる場合、別の証明書で対象サーバーになりすます可能性があります。この設定は短時間の診断に限って使い、有効にしてすぐ接続できたなら証明書検証が問題だと判断できます。結論が出たら無効にし、時刻、SNI、サーバー側の証明書チェーンを修正してください。

失敗ログを保存一時的に有効化1回だけテスト検証エラーを確認無効化して修正

v2rayNでは対象サーバーを編集し、TLS項目でallowInsecureまたは「証明書検証をスキップ」を探します。v2rayNGとv2flyNGでは通常、設定編集画面のTLS設定に「安全でない接続を許可」または類似の項目が表示されます。変更は現在テストするノードだけに行い、すべてのサブスクリプション設定を一括変更しないでください。

ログの結果から問題の所在を特定する

有効な切り分けでは、一度に変更する変数を1つだけにし、変更前後の最初のエラーを記録します。サブスクリプションの再取得、コアの切り替え、SNIの変更、allowInsecureの有効化、ネットワーク変更を同時に行うと、接続が復旧しても本当の原因を特定できません。次回のサブスクリプション更新で問題が再発する可能性もあります。

最小限のテスト環境を用意することをおすすめします。同じノード、同じネットワーク、同じクライアントコアに固定し、時刻の同期、SNIの確認、証明書チェーンの検証、最後にallowInsecureの一時テストという順で進めます。テスト中はノードの自動切り替えを一時的に無効にし、別サーバーのエラーがログに混ざらないようにしてください。

時刻を同期しても証明書が期限切れと表示される?

ログまたはOpenSSLの出力でnotAfterを確認します。期限が現在時刻より前なら、問題はサーバー証明書にあります。クライアントに表示される日付がサーバーの公開結果と異なる場合は、古い入口IPに接続していないかを確認します。

SNIにはノードアドレスとHostのどちらを入力する?

サブスクリプションに明記されたサーバー名を入力します。個別の指定がない場合は、通常、証明書がカバーするドメイン名を使います。ノードのメモだけで判断してはいけません。HostとSNIは用途が異なるため、無理に同じ値へ変更しないでください。

allowInsecureを有効にすると接続できる場合、次に何をする?

有効化前後のログをすぐ保存し、その後に設定を無効にします。具体的なエラーに応じて証明書の有効期限、名前の一致、証明書チェーンを確認し、一時的な診断設定をサブスクリプションに残さないでください。

サブスクリプションを更新しても証明書エラーが直らないのはなぜ?

サブスクリプションで更新されるのは、クライアントが受け取るノードパラメータだけです。サーバーが期限切れの証明書を送信し続けている場合や中間証明書が不足している場合、更新を繰り返してもハンドシェイク結果は変わりません。サーバー側で証明書を更新し、再読み込みする必要があります。

モバイルネットワークだけでハンドシェイクに失敗する場合は?

まずモバイルネットワークと他のネットワークでDNSの解決結果を比較し、システム時刻がネットワークで自動調整されているか確認します。証明書名が変わったり、異なる入口が返されたりする場合は、2つのネットワークのログを保存して比較してください。

最終的な判断は3点に整理できます。時刻の誤りは端末側で修正し、SNIの誤りはサブスクリプションと証明書のドメイン名に合わせて修正します。複数の端末で期限切れの証明書や不完全な証明書チェーンが返る場合は、サーバー側で対処します。この3種類を分けて考えることで、TLSハンドシェイク失敗をポート、UUID、サブスクリプション、ルーティングの問題と誤認せずに済みます。

v2rayN をダウンロード Windows、macOS、Android、Linuxクライアントを確認