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:

  1. 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.
  2. Certificate expired. The server still serves a cert that expired last Tuesday. Renewal job broke.
  3. 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.
  4. 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.
  5. Wrong cert for hostname. Browser hit app.foo.com but the server's only cert covers foo.com. Different from a name mismatch error: depending on how the server's TLS stack reacts, you may get a generic handshake failure instead.
  6. 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.

Want this handled for you? Start free with Domainee — 50 custom domains + 100 GB bandwidth, no card.