SSL: WRONG_VERSION_NUMBER エラーを完全に解決する方法

intermediate🔒 SSL/TLS2026-04-25

エラーを読み解く

SSLエラーほど、Pythonプロジェクトを停滞させるものはありません。現在、ターミナルにこのようなメッセージが表示されているのではないでしょうか。

ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1129)

これは紛らわしいエラーです。TLSやOpenSSLのバージョンが古いことを示唆していますが、それが原因であることは稀です。99%のケースでは、これはプロトコルの不一致が原因です。「暗号化」された通信を期待しているクライアントが、「プレーンテキスト」しか理解できないポートに対して通信を行おうとしています。

根本原因:言語の壁

このエラーは、クライアント(requestsを使用しているPythonスクリプトなど)が、標準的なHTTP用に構成されたサーバーポートに対して、セキュアなTLSハンドシェイクを開始したときに発生します。

コミュニケーションの行き違いとして考えてみましょう。

  • クライアント: 「Client Hello」パケット(暗号化セッションを開始するためのバイナリ文字列)を送信します。
  • サーバー: 暗号化の設定がされていません。バイナリデータを受け取っても認識できず、HTTP/1.1 400 Bad Requestのようなプレーンテキストの文字列で応答します。
  • 不一致: クライアントはその応答の最初の数バイトを確認します。通常はTLSバージョンのヘッダー(ハンドシェイクの場合は0x16など)を期待しますが、代わりに「HTTP」の「H」(16進数で0x48)を受け取ります。OpenSSLは「H-T-T-P」をバージョン番号として解析しようとして失敗し、WRONG_VERSION_NUMBERエラーをスローします。

修正方法

1. URLとポートを監査する

まずは最も明白な原因である、接続文字列のタイポ(打ち間違い)から確認しましょう。ローカルの開発環境では特によくあります。FlaskやDjangoアプリをポート8000で実行している場合、通常、デフォルトではSSLは有効になっていません。

  • 誤り: https://127.0.0.1:8000
  • 正しい例: http://127.0.0.1:8000

本番環境のAPIで非標準ポート(8080や8443など)を使用している場合は、その特定のポートが実際にHTTPSをサポートしているか再確認してください。サイトにSSL証明書があるからといって、そのサーバーのすべてのポートが暗号化されているとは限りません。

2. NginxまたはApacheの設定を確認する

Nginxユーザーは、キーワードの欠落でミスをすることがよくあります。Nginxにポート443でリッスンするように指示しても、SSLを使用するように伝え忘れると、誰もがセキュアであることを期待するポートでプレーンなHTTPを提供してしまいます。

Nginxのサーバーブロックは以下のようになっている必要があります。

server {
    listen 443 ssl; # ここで最も重要なのは 'ssl' キーワードです
    server_name api.example.com;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    location / {
        proxy_pass http://localhost:3000;
    }
}

このsslフラグがないと、Nginxは証明書を無視して生のテキストを送信し、クライアント側でエラーを発生させます。

3. Dockerのポートマッピングを修正する

Dockerでは、ポートが交差する可能性のある別のレイヤーが加わります。よくある間違いは、セキュアな外部ポートを、セキュアでない内部コンテナポートにマッピングしてしまうことです。例えば:

# https://localhost にアクセスしようとすると失敗します
docker run -p 443:80 my-web-app

このコマンドでは、ホストは443でリッスンしますが、トラフィックをコンテナ内のポート80に渡します。コンテナはおそらくポート80でHTTPを提供しているため、ハンドシェイクは即座に失敗します。プロトコルを一致させてください。ホストポートが443であれば、コンテナ化されたアプリが実際にSSL証明書で構成されていることを確認してください。

4. プロキシとロードバランサーのループを解決する

AWS ALB、Cloudflare、またはNginxリバースプロキシを使用している場合、「SSLオフロード(SSL Offloading)」が発生している可能性があります。これはプロキシがHTTPS接続を処理し、バックエンドとはHTTPで通信する仕組みです。バックエンドのコードが、ポート80でしかリッスンしていない別の内部サービスに対してHTTPS接続を強制しようとすると、このエラーが発生します。

修正の確認

推測に頼らず、curlを使用してポートを直接テストしてください。-v(verbose)フラグを使用すると、エラーが発生する前にサーバーが何を返しているかを正確に確認できます。

curl -vI https://your-api-endpoint.com

出力に * error:1408F10B:SSL routines:ssl3_get_record:wrong version number が含まれている場合、サーバーは間違いなくプレーンテキストを送信しています。また、OpenSSLを使用して生のレスポンスを確認することもできます。

openssl s_client -connect your-api-endpoint.com:443

CONNECTED(00000003) が表示された後にハングしたり、プレーンテキストのHTTPエラーが表示されたりする場合、問題はPythonコードではなくサーバーの設定にあります。

予防のヒント

設定エラーは、複雑なYAMLやJSONファイルの中に隠れていることがよくあります。この YAML ↔ JSON Converter のようなバリデーターを使用すると、Docker ComposeやKubernetesのマニフェストが本番環境にデプロイされる前に、構造的な問題をキャッチするのに役立ちます。

  • 環境変数: https:// をハードコードしないでください。変数を使用して、ローカルテスト用の http と本番用の https を切り替えられるようにします。
  • 明示的に指定する: デバッグ時は、意図しないデフォルト値に当たらないよう、常にURLにポート(例::443)を含めてください。
  • ヘッダーの監視: X-Forwarded-Proto ヘッダーを使用して、プロキシがHTTP経由で通信している場合でも、元のリクエストがセキュアであったかどうかをアプリケーションに知らせるようにします。

Related Error Notes