TLS handshake failed
The error that fires when a browser and server can't agree on how to set up a TLS connection. Usually means cert, protocol, or cipher mismatch.
TLS_HANDSHAKE_FAILED (or ERR_SSL_PROTOCOL_ERROR, or tls: handshake failure depending on what's reading the connection) is what you get when the client and server tried to set up a TLS connection and bailed before they finished exchanging keys.
The handshake is the dance between client and server to agree on: TLS version, cipher suite, certificate validity, SNI hostname. Any one of these failing kills the whole thing.
The common causes
In rough order of how often you'll see them:
- No SNI sent. The client is too old to send SNI, the server uses SNI to pick a cert, falls back to a default cert that doesn't match the requested hostname. Mostly affects Java 6, very old Android, headless bots.
- Certificate expired. The server still serves a cert that expired last Tuesday. Renewal job broke.
- Cipher suite mismatch. Client only supports modern AEAD ciphers, server only configured for older RSA-based ones (or vice versa). Common when one side is locked down for compliance.
- TLS version mismatch. Server requires TLS 1.2+, client maxes out at TLS 1.0. Or server only does TLS 1.3 and client predates it.
- Wrong cert for hostname. Browser hit
app.foo.combut the server's only cert coversfoo.com. Different from a name mismatch error: depending on how the server's TLS stack reacts, you may get a generic handshake failure instead. - Connection killed mid-handshake. Some intermediate (firewall, IDS, load balancer) dropped the connection. The client sees the handshake never complete.
How to debug
openssl s_client -connect example.com:443 -servername example.com -showcerts
Or use the SSL Labs test (https://www.ssllabs.com/ssltest/) for a thorough report on what protocols and ciphers the server accepts.
In a custom-domain SaaS
When your customers report TLS_HANDSHAKE_FAILED on their branded URL, 90% of the time the issue is on their side: stale CDN proxy in front of your edge, corporate firewall doing TLS inspection, or their device's clock skewed by hours (which makes the not-yet-valid date check fail). Provide them with a one-line openssl s_client command so they can reproduce it from the same network and see the real cause.