SSL “wrong version number”: you’re speaking TLS to something that isn’t

Despite the wording, this almost never has anything to do with TLS versions. Your client started a TLS handshake and the other side answered in plain text.

What the error looks like

curl: (35) error:0A00010B:SSL routines::wrong version number          # OpenSSL 3
curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number   # OpenSSL 1.1
requests.exceptions.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1006)
Error: write EPROTO ... error:0A00010B:SSL routines::wrong version number   # Node

Why it says “version”

Every TLS message starts with a 5-byte record header: a content type (0x16 for a handshake) and a protocol version such as 0x03 0x03. When the server replies with plain text like HTTP/1.1 400 Bad Request, the client reads the letters H, T, T as that header. T and P (0x54, 0x50) are not a valid version, hence “wrong version number”. You can check those byte values in the number base converter.

The usual causes

SituationWhat’s wrongFix
https://localhost:3000, https://host:8080The dev server or app speaks plain HTTP on that portUse http://, or put TLS in front of it (a reverse proxy)
https://example.com:80Port 80 is plain HTTPDrop the port: https://example.com uses 443
HTTPS_PROXY=https://proxy:3128Most proxies accept plain HTTP and tunnel TLS with CONNECTHTTPS_PROXY=http://proxy:3128 (yes, http even for https traffic)
Python smtplib.SMTP_SSL(host, 587), Nodemailer secure: true on 587587 starts in plain text and upgrades with STARTTLSPort 587: SMTP(host, 587) then starttls() / secure: false. Port 465: SMTP_SSL / secure: true
Database or Redis URL with TLS on (rediss://, sslmode=require) against a server without TLSSame mismatch, different protocolMatch the client setting to the server, or enable TLS on the server
Corporate network, hotel Wi-Fi, captive portalA middlebox answers with an HTML page instead of TLSSign in to the portal or use the network’s proxy settings
Load balancer listener on 443 set to HTTPTLS is expected but the listener is plainSet the listener protocol to HTTPS/TLS with a certificate

Prove it in ten seconds

# Does the port speak plain HTTP?
curl -v http://host:8080/

# What does the port send back to a TLS hello?
openssl s_client -connect host:8080 -servername host
# "wrong version number" here too means: not TLS on that port

# Which proxy settings are in play?
env | grep -i _proxy

If plain http:// works, that’s your answer. If neither works, check the port is right with ss -ltnp on the server (see localhost refused to connect).

Not the same as…

Questions

What does SSL wrong version number mean?

Your client sent a TLS handshake and got back something that is not TLS, usually a plain HTTP response. The client reads the first bytes of that text as a TLS version field and finds nonsense. It rarely has anything to do with actual TLS versions.

How do I fix wrong version number in curl?

Check the URL: use http:// for ports that serve plain HTTP, drop :80 from https URLs, and make sure HTTPS_PROXY or https_proxy starts with http:// unless your proxy really accepts TLS connections.

Why does smtplib give WRONG_VERSION_NUMBER on port 587?

Port 587 starts in plain text and switches to TLS with the STARTTLS command. SMTP_SSL starts TLS immediately, which only works on port 465. Use smtplib.SMTP(host, 587) followed by starttls().

Is wrong version number a certificate problem?

No. Certificate errors happen after a successful handshake. Wrong version number happens before any certificate is exchanged, because the other side is not speaking TLS at all.

Published .

Try it

Related guides

Newest guides