Error message
SSL routines:tls_validate_record_header:wrong version number
Also printed as
- error:0A00010B:SSL routines:ssl3_get_record:wrong version number
- error:0A00010B:SSL routines::wrong version number
- error:1408F10B:SSL routines:ssl3_get_record:wrong version number
OpenSSL 3.5 names the function tls_validate_record_header, OpenSSL 3.0 ssl3_get_record ("error:0A00010B:SSL routines:ssl3_get_record:wrong version number"), OpenSSL 1.1.1 uses the older code 1408F10B. Applications that format errors without the function name (curl, Node.js messages) print "error:0A00010B:SSL routines::wrong version number". The prefix before "error:" is a thread id and the suffix a source location; both vary.
OpenSSL 3 (openssl s_client and any OpenSSL-based client) — SSL_R_WRONG_VERSION_NUMBER
What it means
OpenSSL expected a TLS record and checked the header's protocol version field, but the bytes it received are not TLS at all — usually the start of an HTTP response or a mail server's text greeting, read as if it were a version number. The name is misleading: the peer is speaking a different protocol (or plain text) on that port, not an unsupported TLS version.
Why it happens
The port serves plain HTTP or another plain protocol
commonHTTP on 80/8080, a backend behind a TLS-terminating proxy, or a service whose TLS listener is on another port.
A STARTTLS port was treated as implicit TLS
commonSMTP (25, 587), IMAP (143), POP3 (110), LDAP (389), PostgreSQL, MySQL and XMPP start in plain text and switch to TLS on request; a direct TLS handshake reads their greeting as garbage.
A proxy or captive portal answers in plain text
occasionalClients configured with an https:// proxy URL for a plain-HTTP proxy, or networks that intercept connections, return HTTP where TLS was expected.
How to fix it
- 1.
Use -starttls for in-band upgrade protocols
openssl s_client supports smtp, imap, pop3, ftp, xmpp, ldap, postgres, mysql and others; with it the TLS handshake starts after the protocol's upgrade command.
bash # Protocols that upgrade in-band need -starttls openssl s_client -connect mail.example.com:587 -starttls smtp -servername mail.example.com </dev/null openssl s_client -connect db.example.com:5432 -starttls postgres </dev/null openssl s_client -connect ldap.example.com:389 -starttls ldap </dev/null - 2.
Look at what the port actually sends
A text banner or an HTTP status line from netcat shows that the port is not implicit TLS.
bash # What does the port answer with? A readable banner means it is not TLS printf 'HEAD / HTTP/1.0\r\n\r\n' | timeout 3 nc api.example.com 8080 | head -3 timeout 3 nc mail.example.com 587 </dev/null | head -1 # "220 ... ESMTP" = SMTP, STARTTLS port - 3.
Connect to the implicit-TLS port
Use the protocol's TLS port when the client cannot do STARTTLS.
bash # Implicit TLS ports: 443 (HTTPS), 465 (SMTPS), 993 (IMAPS), 995 (POP3S), 636 (LDAPS) openssl s_client -connect mail.example.com:465 -servername mail.example.com -brief </dev/null
Related errors
curl: (35) TLS connect error: error:0A00010B:SSL routines::wrong version numbercurl (OpenSSL backend)[SSL: WRONG_VERSION_NUMBER] wrong version numberPython ssl module (OpenSSL)code: 'ERR_SSL_WRONG_VERSION_NUMBER'Node.js tls.connect / fetch (undici)tls: first record does not look like a TLS handshakeGo crypto/tls