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
| Situation | What’s wrong | Fix |
|---|---|---|
https://localhost:3000, https://host:8080 | The dev server or app speaks plain HTTP on that port | Use http://, or put TLS in front of it (a reverse proxy) |
https://example.com:80 | Port 80 is plain HTTP | Drop the port: https://example.com uses 443 |
HTTPS_PROXY=https://proxy:3128 | Most proxies accept plain HTTP and tunnel TLS with CONNECT | HTTPS_PROXY=http://proxy:3128 (yes, http even for https traffic) |
Python smtplib.SMTP_SSL(host, 587), Nodemailer secure: true on 587 | 587 starts in plain text and upgrades with STARTTLS | Port 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 TLS | Same mismatch, different protocol | Match the client setting to the server, or enable TLS on the server |
| Corporate network, hotel Wi-Fi, captive portal | A middlebox answers with an HTML page instead of TLS | Sign in to the portal or use the network’s proxy settings |
| Load balancer listener on 443 set to HTTP | TLS is expected but the listener is plain | Set 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…
- certificate has expired or unable to get local issuer certificate: the TLS handshake worked and the certificate failed checks. See certificate has expired.
- unsupported protocol, tlsv1 alert protocol version or no protocols available: these really are about TLS versions (an old server that only offers TLS 1.0, or a client with a high minimum). Update the server.
- sslv3 alert handshake failure: both sides speak TLS but share no cipher, or the server wanted a client certificate.
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 .