はじめに
本章の目的
この章では、本記事全体の目的と読み方をやさしく説明します。対象となるエラーは主にWebサーバーやリバースプロキシ(特にNginx)で発生する「ssl_do_handshake() failed」です。原因特定や対応手順を体系的に学べるよう構成しています。
想定読者
- サーバー運用者・開発者
- リバースプロキシを使う技術者
- エラー対応に不安がある初心者
専門知識が深くなくても、手順を追えば対応できるよう配慮しています。
本記事で得られること
- エラーの全体像と発生状況の理解
- 代表的なエラーコードと意味の把握
- 具体的な対処方法と実践的な手順
- 調査・デバッグの進め方やベストプラクティス
読み進め方の提案
まず第2〜4章で基礎を押さえ、第5章で対処法を試してください。ログや設定ファイルを確認する際は、事前にバックアップを取ってください。
ssl_do_handshake() failed エラーとは
概要
「ssl_do_handshake() failed」は、WebサーバーやリバースプロキシがSSL/TLSのハンドシェイクを完了できなかったときに出るエラーメッセージです。クライアント側には502 Bad Gatewayなどで返ることが多く、サーバーログに詳細な原因を示す文言が残ります。
何が起きているか(平易な説明)
ハンドシェイクは、安全な通信を始めるための「あいさつ」のような手順です。ここでは、使用するTLSのバージョンや暗号方式、サーバーの証明書のやり取りを行います。どれかが合わないと握手が失敗し、接続が成立しません。
よく見られるログ例(実例)
- “ssl_do_handshake() failed”
- “wrong version number”
- “bad key share”
- “tlsv1 unrecognized name”
これらは原因の手掛かりになります。
影響と最初に確認すること
影響は接続の中断やページ表示の失敗です。まずは、対象サーバーが応答しているか、証明書の有効期限やチェーン、TLSバージョンやポート設定(HTTPS用のポートか)を確認してください。これらが分かれば次の対処が見えてきます。
主な発生原因
SSL/TLS のハンドシェイクが失敗する代表的な原因を、分かりやすく説明します。各項目で簡単な確認方法や例も付けています。
アップストリームポートの誤設定
HTTPS 通信で誤って HTTP ポート(例:80)を指定すると、相手がプレーンテキストを返しハンドシェイクが成立しません。確認方法:接続先ポートが 443(または意図した TLS ポート)かを設定ファイルで確認します。
SNI(Server Name Indication)ミスマッチ
クライアントが送るホスト名とサーバーの証明書のホスト名が一致しないと、正しい証明書が返らず失敗します。例:仮想ホストでサブドメインを指定していない場合。確認方法:curl -v –resolve や openssl s_client で SNI を指定して試します。
証明書の検証エラー
自己署名証明書や未登録の CA、途中のチェーン欠如で検証が通りません。確認方法:ブラウザの証明書情報や openssl s_client -showcerts でチェーンを確認します。
SSL/TLS プロトコルや暗号スイートの不一致
サーバーとクライアントが共通のプロトコル(TLS1.2/1.3 など)や暗号スイートを持たないと成立しません。例:サーバーが TLS1.3 のみ許可で古いクライアントが対応していない場合。確認方法:TLS バージョンとサポートする暗号をログやテストツールで確認します。
システム時刻のズレ
端末の時刻が正しくないと、証明書の有効期限チェックに失敗します。確認方法:date コマンドで時刻を確認し、NTP を使って同期します。
不正なクライアントリクエストや悪質な BOT アクセス
怪しいリクエストやプロトコルに沿わないアクセスはハンドシェイクを壊します。確認方法:アクセスログやファイアウォールログで異常な接続元やパターンを調べます。
各原因は環境によって重複することがあります。まずログと簡単な接続テストで切り分けると原因特定が速くなります。
よく見られるエラーコード例と意味
error:0A00006C:SSL routines::bad key share
このエラーは主にTLS1.3の鍵共有(key share)が合わない場合に出ます。クライアントとサーバで使える楕円曲線(named group)が一致しないか、クライアントが送った鍵情報をサーバが受け付けないことが原因です。確認項目:クライアント・サーバのTLS1.3対応状況、supported groups設定、ミドルボックス(負荷分散器など)が鍵交換を変えていないか。
error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name
これはSNI(サーバ名表示)に関連する問題です。クライアントが送ったホスト名がサーバやプロキシで認識されず、期待した証明書や仮想ホストに到達できないと発生します。確認項目:クライアントのSNI送信(例:openssl s_client -servername)、サーバの仮想ホスト設定、逆プロキシやロードバランサのSNI処理。
wrong version number
多くはTLSで接続すべき先に平文HTTP接続してしまった場合に起こります。ポート番号やプロトコルの間違い、誤ったリダイレクト、または中間機器が通信を変換していることが原因です。確認項目:接続先のポートとプロトコル、プロキシ設定、サーバが期待するプロトコル(HTTP/1.1, HTTP/2, TLSバージョン)。
その他よく見る短い対処ヒント
- openssl s_client を使い、-connect と -servername で手動検証する。
- サーバログとTLS設定(supported groups, ciphers, ALPN)を照合する。
- 負荷分散器やプロキシを一時的に迂回して直接接続を試す。
具体的な解決策・対処法
以下は、ssl_do_handshake() failed を解消するための具体的な手順です。順に確認していくと原因を特定しやすくなります。
1) アップストリームのポート設定確認
- HTTPSで接続する場合、アップストリームにポート443を指定してください。例: proxy_pass https://backend:443; もしくは upstream で正しいポートを設定します。
- curlやtelnetでポートが開いているか確認します: curl -vk https://backend:443/ または telnet backend 443
2) SNI対応の有無と設定確認
- 接続先がSNIを要求する場合、Nginxでは proxy_ssl_server_name on; を有効にします。必要に応じて proxy_ssl_name $host; を追加してください。
- opensslでSNI付きの接続を試せます: openssl s_client -connect backend:443 -servername example.com
3) 証明書の有効性とCAチェーン設定
- 証明書の有効期限を確認します: openssl x509 -in cert.pem -noout -dates
- 中間証明書が抜けていないか確認し、サーバーに fullchain(または中間含むチェーン)を設定してください。
4) TLSプロトコル・暗号スイートの互換性確認
- サーバーとクライアントで許可するプロトコルを合わせます。Nginx例: proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_ssl_ciphers “HIGH:!aNULL”;
- 特定のプロトコルや暗号で接続を試す: openssl s_client -connect backend:443 -tls1_2
5) サーバー・クライアントのシステム時刻補正
- 証明書の検証は時刻に依存します。時刻がずれていると失敗します。時刻を同期してください: timedatectl set-ntp true または ntpdate pool.ntp.org
6) ログ・デバッグの活用
- Nginxのエラーログを詳細に出す: error_log /var/log/nginx/error.log debug; を一時的に有効にして再現します。
- curl -v や openssl s_client の出力も有用です。ログを見比べて、どの段階でハンドシェイクが止まるかを特定してください。
これらを順に確認すると、多くの場合で問題を解消できます。必要であれば各手順のコマンド例や設定例をさらに詳しく示しますので、お知らせください。
その他のベストプラクティス
1. SSL/TLS設定の一貫性を維持する
- サーバーや負荷分散装置、APIゲートウェイで同じプロトコル(例:TLS1.2/1.3)と安全な暗号套件(例:ECDHE)を使います。設定は手動で変えず、構成管理ツール(AnsibleやChefなど)で配布すると差異を防げます。
2. 定期的な証明書更新と監視体制の整備
- 証明書の有効期限切れはよくある原因です。自動更新(ACME/Let’s Encryptや商用の自動化)を利用し、期限を過ぎそうな場合にアラートが出る監視を必ず入れてください。中間証明書の入れ忘れも接続失敗を招くため、チェーンの確認も定期的に行います。
3. リアルタイム監視ツールの活用
- 通信の成功率やハンドシェイク時間を可視化します。合成監視(定期的に実際に接続を試す)とログ監視を組み合わせると早期検知できます。アラートは閾値に基づいて緊急度を分け、自動復旧やスイッチオーバーの仕組みと連携させると効果的です。
4. 運用上の注意点
- 本番とステージングで設定を合わせ、変更は段階的にロールアウトします。鍵のローテーションやOCSPステープリング、有効な暗号の定期見直しも忘れず行ってください。
これらを習慣化することで、ssl_do_handshake() failed の発生を大幅に減らせます。
発生時の調査・デバッグ手順例
以下は実際にssl_do_handshake() failedが出たときの調査手順例です。順に実行して原因を絞り込んでください。
- サーバーログ確認
- Nginx等のエラーログを確認します。例: tail -n 200 /var/log/nginx/error.log
-
タイムスタンプで問題発生時刻を合わせ、”SSL”や”handshake”関連の行を探します。
-
アップストリームのポート・プロトコル確認
- Nginx設定でproxy_passやupstreamのプロトコル(http:///https://)とポートを確認します。
-
設定ファイルを探す: grep -R “proxy_pass” /etc/nginx -n
-
SNI設定と証明書チェーン確認
-
バックエンドがSNIを要求するか確認します。サーバ名が正しく渡されているかチェックします。
-
opensslで接続テスト
- 基本コマンド: openssl s_client -connect host:port -servername host
-
出力の”Verify return code”、証明書チェーン、選択されたcipherやALPNを確認します。
-
必要に応じてNginxのデバッグログ有効化
- error_log /var/log/nginx/error.log debug; を設定してNginxを再読み込みします。ログは詳しく出ますので、解析後に戻してください。
補足
– 再現テストはcurl -vやtcpdumpで通信をキャプチャすると原因特定が早まります。
– 各手順で得られたメッセージを控え、順に潰していくと効率的です。












