できない.dev

OpenSSL「wrong version number」の原因と直し方

wrong version number は、URL の http と https をポートに合わせるか、サーバーの listen に ssl を付ければ直る。
TLS で話しかけた相手が平文で応答すると出るエラーで、curl の (35) も同じ原因である。

公開:

要約

wrong version number は、TLS で話しかけた相手から TLS ではない応答が返ったときに OpenSSL が出すエラーである。
名前から TLS のバージョンの不一致を疑いやすいが、多くは平文で待ち受けているポートへ TLS で接続していることが原因である。curl -k は証明書の検証を切るだけなので、付けても同じエラーのまま変わらない。

OpenSSL を使うツールごとに、同じ原因が次のように表示される。

実行したもの表示
curl 8.14・OpenSSL 3.5curl: (35) TLS connect error: error:0A00010B:SSL routines::wrong version number
curl 7.88・OpenSSL 3.0curl: (35) OpenSSL/3.0.22: error:0A00010B:SSL routines::wrong version number
curl 8.5・OpenSSL 1.1.1curl: (35) OpenSSL/1.1.1w: error:1408F10B:SSL routines:ssl3_get_record:wrong version number
Python 3.13 の urllib[SSL: WRONG_VERSION_NUMBER] wrong version number
Node.js 22 の fetchERR_SSL_WRONG_VERSION_NUMBER
nginx の proxy_pass https://SSL_do_handshake() failed (SSL: error:0A00010B:SSL routines::wrong version number)

相手のポートが TLS を話しているかどうかは、openssl s_client で分かる。

openssl s_client -connect example.com:443 </dev/null

TLS のポートなら、subject= の行と New, TLSv1.3 のような行が出る。
平文のポートなら wrong version number と no peer certificate available が出る。

解決策

1. スキームとポートを合わせる

同じポートに http:// で接続して通常の応答が返るなら、そのポートは平文で待ち受けている。

curl -sS http://example.com:443/

通常の応答が返ったら、URL を http:// に直すか、TLS を待ち受けているポートへ接続先を変える。
TLS のポートへ http:// で接続した場合は、nginx なら 400 The plain HTTP request was sent to HTTPS port が返るので区別できる。

2. nginx の listen に ssl を付ける

server {
    listen 443 ssl;
    ssl_certificate /etc/nginx/server.crt;
    ssl_certificate_key /etc/nginx/server.key;
}

nginx -s reload のあと、openssl s_client で subject= の行が出ることを確かめる。

3. HTTPS_PROXY を http:// で始める

HTTPS_PROXY は「HTTPS の通信に使うプロキシ」を指す変数で、プロキシ自身のスキームは別に決まる。
平文で待ち受けるプロキシなら http:// で書く。

export HTTPS_PROXY=http://proxy.example.com:8888

4. STARTTLS のポートには -starttls を付ける

-starttls を付けると、s_client がプロトコルごとの手順を踏んでから TLS へ切り替える。

openssl s_client -connect mail.example.com:587 -starttls smtp </dev/null

よくある原因

  1. https:// の URL で平文のポートへ接続している。https://example.com:80 や、HTTP で動かしている開発サーバーのポートへの接続で起きる。
  2. nginx の listen 443; に ssl が無い。
    証明書を書いていても、ssl が無ければそのポートは平文の HTTP で応答する。
  3. HTTPS_PROXY の値が https:// で始まっている。
    プロキシとの間の通信まで TLS になるので、平文の応答を返すプロキシに対してはこのエラーになる。
    tinyproxy のように何も返さないプロキシでは、エラーにならずタイムアウトになる。
  4. SMTP の 587 番のように、平文で始めて途中から TLS に切り替えるポートへ、最初から TLS で接続している。
    サーバーの最初の挨拶が平文なので、ここでも同じエラーになる。