V2RayのTLSハンドシェイク失敗・証明書エラーの対処法|システム時刻、SNI、allowInsecureを確認

V2RayのTLS証明書エラーについて、時刻のずれやSNIと証明書の不一致、ドメイン設定ミスなどの原因と、クライアントでの確認箇所を解説します。

この記事の要点

通常のTLSを使用するノードで、クライアントログにハンドシェイク失敗やx509証明書エラーが表示される場合が対象です。「ログの内容 → システム時刻 → 接続先とSNI → 証明書チェーン → クライアントの設定項目」の順に確認し、ネットワークのタイムアウトと証明書検証エラーを切り分けます。証明書検証は有効のままにし、項目を一つずつ変更してログで結果を確認してください。

証明書エラーと接続失敗を切り分ける

「TLSハンドシェイク失敗」は処理段階を示すもので、原因を特定するエラーではありません。クライアントはまずノードのアドレスとポートに接続し、TLSネゴシエーションと証明書検証を行ってから、プロキシプロトコルの通信に進みます。接続タイムアウト、サーバー側からの切断、証明書のドメイン不一致などが、まとめて「接続失敗」と表示されることがあります。調査時は速度測定の失敗表示だけで判断せず、詳細なコアログを開き、エラーの末尾を確認してください。

v2rayNではメイン画面のログ欄から今回の接続ログを確認できます。v2rayNGとv2flyNGでは、メインメニューのログ項目を開きます。リリースによって項目名や場所が異なる場合があります。クライアントのバージョン、コアの名称、トランスポート方式、エラー全文を記録したうえで、対象ノードだけに一度リクエストを送り、複数のバックグラウンド接続のログが混ざらないようにしてください。

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

原因:検証時刻が証明書の有効期間外です。端末の時刻がずれている場合と、サーバー証明書そのものが期限切れの場合があります。対処:まず端末の時刻を同期し、ログに記録された現在時刻と証明書の有効期間を照合します。解消しない場合は、サーバー側で証明書を更新して再読み込みします。

エラー:x509: certificate signed by unknown authority

原因:証明書から信頼されたルート証明書までのチェーンを構築できません。中間証明書の不足、自己署名証明書、接続の差し替えなどが考えられます。対処:サーバーが証明書チェーンを正しく送信しているか、端末の信頼ストアに問題がないかを確認し、発行者も照合してください。検証を無効にして回避するのではなく、原因を修正します。

エラー:tls: first record does not look like a TLS handshake

原因:接続先ポートからTLSデータではなく、別の応答が返っています。平文のサービスに接続している可能性があります。対処:ノードのポート、トランスポートのセキュリティ設定、リバースプロキシの待ち受け設定を確認してください。

エラー:context deadline exceeded

原因:処理がタイムアウトしたことを示すエラーで、この表示だけでは証明書の問題と判断できません。対処:前後のログも確認し、名前解決、TCP接続、ハンドシェイクのどの段階で止まったかを調べます。まず証明書関連の設定を変更するのは避けてください。

システム時刻:同期してから証明書の期限を確認

証明書には有効開始時刻と有効期限があります。端末の時計が進んでいると「まだ有効ではない」、遅れていると「期限切れ」と判定されることがあります。タイムゾーンを変えるだけでは、実際のシステム時刻は通常変わりません。修正が必要なのは実際の日付と時刻です。デュアルブート、長期間の電源オフ後の起動、自動時刻同期の失敗があれば、優先して確認してください。

  1. 時刻を同期する

    Windows 11:「設定」→「時刻と言語」→「日付と時刻」を開き、「時刻を自動的に設定する」をオンにします。「その他の設定」で「今すぐ同期」をクリックしてください。同期できない場合は、まず端末のネットワーク接続を確認します。

  2. ほかの端末も確認する

    macOS:「システム設定」→「一般」→「日付と時刻」を開き、日付と時刻の自動設定をオンにします。Android:「設定」→「システム」→「日付と時刻」を開き、自動設定をオンにします。メーカー独自のOSでは、「その他の設定」に項目がある場合があります。

  3. 接続をやり直す

    時刻を同期したら、クライアントの接続をいったん停止して再起動し、新しいログを記録します。時刻を修正する前の古いログで、修正の効果を判断しないでください。

  4. 証明書の有効期間を確認する

    端末の時刻が正しいのに期限切れと表示される場合は、実際に受信した証明書の有効期間を確認してください。証明書を更新した後は、TLS終端サービスの再読み込みも必要です。ディスク上のファイルが新しくなっていても、接続に新しい証明書が使われているとは限りません。

接続先アドレス、SNI、Hostを混同しない

ノード編集画面の「アドレス」は接続先を指定します。SNIはTLS ClientHelloで送信するサーバー名で、サーバーはこれを使って証明書を選ぶことがあります。WebSocketのHostは、TLSハンドシェイク後のHTTPリクエストヘッダーです。3つに同じドメインを使う場合も、別の値を使う場合もありますが、サーバーの設定に従う必要があります。「偽装ドメイン」を自由に差し替えられる項目と考えないでください。

設定項目適用される段階確認ポイント
アドレスとポートネットワーク接続サーバーから指定された接続先を使用します。ドメインの名前解決結果が、想定したサーバーを指していることを確認してください。
serverName / SNITLSハンドシェイク証明書に記載されたサーバー名を入力します。プロトコル、ポート、パスは含めません。
WebSocket HostHTTP Upgradeリクエストリバースプロキシの仮想ホストと一致させます。TLSのSNIの代わりにはなりません。
WebSocket pathHTTPパスの照合サーバーで指定されたパスを使用します。パスの誤りは通常、証明書のドメインエラーとは別の問題です。
allowInsecure証明書の検証一般的な公開証明書を使う場合は、falseのままにします。

説明用に予約されたドメインedge.example.comを例にします。証明書のSubject Alternative Nameにこの名前が含まれている場合、SNIにはこのホスト名を指定します。https://edge.example.com/pathのようなURLを入力してはいけません。IPアドレスで接続する場合も、サーバーが要求するドメイン名をSNIに指定できます。IPアドレスで直接証明書を検証するには、証明書に一致するIPアドレスの項目が必要です。ドメイン名だけでは不十分です。

443
HTTPSでよく使われるポートですが、すべてのノードで固定されているわけではありません。
80
平文HTTPでよく使われるポートです。この番号だけを理由にTLSを有効にしないでください。

判断のポイント:ポート番号から推測せず、サーバーの設定と照合する

ポート番号は慣例にすぎません。まずノードで通常のTLSが有効か確認し、次にSNIと証明書のドメイン名を照合してください。WebSocketのHostを変えても、HTTPリクエストより前に発生する証明書名のエラーは解消できません。

クライアントでSNIとallowInsecureを確認する場所

画面の項目名はバージョンによって異なるため、ここではノード編集画面と実際の設定項目を目印にします。サブスクリプションから取り込んだ設定は更新時に上書きされることがあるため、変更前にノードを複製して値を記録してください。一度のテストでアドレス、ポート、SNI、トランスポート方式を同時に変更すると、接続が復旧しても原因を特定しにくくなります。

以下は通常のTLSで使用するstreamSettingsの設定例です。項目を確認するための抜粋であり、ノード設定全体ではありません。例ではTCPを使用し、ドメイン名は説明用です。既存ノードがWebSocketなど別のトランスポートを使っている場合、例に合わせてTCPへ変更しないでください。利用できる項目は、実際に動作しているコアの仕様を確認してください。

{
  "streamSettings": {
    "network": "tcp",
    "security": "tls",
    "tlsSettings": {
      "serverName": "edge.example.com",
      "allowInsecure": false
    }
  }
}

証明書チェーンとSNI:個別のTLS接続で原因を絞り込む

ターミナルを使える環境では、同じ端末・同じネットワークからサーバーが返す証明書を個別に確認できます。以下のコマンドを実行するには、システムにOpenSSLがインストールされている必要があります。実行前に、例のホスト名、SNI、ポートを実際の値に置き換えてください。接続先アドレスとSNIは別々に指定するため、「IPアドレスで接続し、ドメイン名で検証する」設定の確認にも便利です。

openssl s_client -connect edge.example.com:443 -servername edge.example.com -showcerts -verify_hostname edge.example.com -verify_return_error

特定のネットワークだけで発行者の異なる証明書が返される場合は、そのネットワークでHTTPS検査、ポータルへのログイン、通信の転送が行われていないか確認してください。エラーダイアログに表示された情報をもとに、出所不明のルート証明書を安易にインストールしないでください。サーバーを他者が管理している場合は、個人情報を除いたエラー末尾、発生時刻、SNI、ポートを伝えます。サブスクリプションの完全なURL、UUIDなどの認証情報は共有しないでください。

よくある質問と修正後の確認

allowInsecureを有効にしたら接続できました。このまま使ってもいいですか?

通常の証明書検証が関係している可能性を示すだけで、ドメインの問題が確定したわけではありません。時刻を同期し、SNI、証明書の有効期間、完全な証明書チェーンを確認してください。修正後はこの設定をオフにし、実際にリクエストを送って接続を確かめます。

ブラウザーではノードのドメインにアクセスできますが、クライアントでエラーになるのはなぜですか?

まず、同じアドレス、ポート、SNIに接続しているか確認してください。ブラウザーでは別のプロキシを経由したり、異なる接続先に名前解決されたりすることがあります。使用する信頼ストアも異なる場合があります。クライアントが実際に接続した先と、受信した証明書を基準に確認してください。

サブスクリプションを更新するとSNIが元に戻ります。どうすればいいですか?

テスト用にノードを複製し、変更の効果を確認してから、サブスクリプションの提供元に配信設定の修正を依頼してください。更新のたびに元のノードも確認し、自動上書きをクライアントの保存失敗と取り違えないようにします。

ノードがREALITYの場合も、これらのTLS設定を確認すべきですか?

まずsecurityの実際の値を確認してください。REALITYには通常のTLSの証明書修復手順をそのまま適用できません。サーバーが指定するserverName、公開鍵、shortIdなどの項目を確認してください。通常のTLSに切り替えたり、検証をスキップしたりして解決を試みないでください。

変更後はノードを保存し、接続を再起動してから、同じリクエストで再テストしてください。遅延の数値だけでなく、ログに元の証明書エラーが出ないこと、実際のリクエストが完了すること、通常のTLSノードでは証明書検証が有効のままであることを確認します。ハンドシェイク後にパスエラー、異常なHTTPステータスコード、プロトコル認証エラーが発生する場合は、トランスポートのパスやアカウント設定を調べます。SNIを何度も変更する必要はありません。

クライアントをダウンロード