TLS handshake failed means the client and server couldn’t establish a TLS connection. The handshake failed at one of: protocol negotiation, cipher selection, certificate verification, or hostname matching. The team that uses Sectigo’s troubleshooting guide and checks the seven common causes resolves 95% of these in minutes.
Table of contents
- The handshake steps
- Cause 1: expired certificate
- Cause 2: TLS version mismatch
- Cause 3: cipher suite mismatch
- Cause 4: missing intermediate cert
- Cause 5: hostname mismatch
- Cause 6: clock skew
- Cause 7: firewall / DPI
- Debugging with openssl
- FAQ
The handshake steps
TLS handshake has 4 steps that can fail:
- ClientHello / ServerHello: protocol version (TLS 1.2 vs 1.3) and cipher suite.
- Server certificate: chain validation, expiry, hostname.
- Key exchange: client key share, server key share.
- Finished: both sides verify the handshake.
The team that has verbose error messages from openssl s_client or browser dev tools finds the failing step.
Cause 1: expired certificate
The cert has passed its expiry date:
# Check cert expiry
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates
The team that sees notAfter in the past has an expired cert. Fix: renew.
Cause 2: TLS version mismatch
Server only supports TLS 1.0/1.1, client only supports TLS 1.2/1.3:
# Test specific TLS version
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
The team that sees handshake failure only with old TLS versions has server-side protocol mismatch. Fix: enable modern TLS on server.
Cause 3: cipher suite mismatch
Client and server have no common cipher:
# See offered ciphers
openssl s_client -connect example.com:443 -cipher 'ALL'
The team that has a strict cipher config (e.g., only AEAD ciphers) and an old client has mismatch. Fix: relax server config or upgrade client.
Cause 4: missing intermediate cert
Server presents leaf cert but not the intermediate. Client can’t verify chain:
# Verify chain
openssl s_client -connect example.com:443 -showcerts
# Look for "verify return code"
The team that sees unable to get local issuer certificate has missing intermediate. Fix: install full chain on server.
Cause 5: hostname mismatch
Cert is for example.com but client connected to www.example.com:
openssl s_client -connect www.example.com:443 -servername www.example.com
# Look for "verify return code: 62" (hostname mismatch)
The team that uses the wrong hostname or has a cert without both names has mismatch. Fix: SAN with both hostnames.
Cause 6: clock skew
Client or server clock is wrong:
date
# Check on both sides
The team that has a clock 5+ minutes off has handshake failure on cert validation. Fix: NTP.
Cause 7: firewall / DPI
Corporate firewall or middlebox intercepts TLS and breaks handshake:
# Test from outside (different network)
curl -v https://example.com/
The team that sees the failure only on certain networks has a middlebox. Fix: try from unrestricted network.
Debugging with openssl
Verbose:
openssl s_client -connect example.com:443 -servername example.com -debug -msg
The team that uses -debug -msg sees the handshake messages.
FAQ
What’s the difference between ‘handshake failed’ and ‘connection refused’?
Connection refused = TCP couldn’t connect (service down, firewall). Handshake failed = TCP connected but TLS failed (cert, cipher, version issue).
Why does the browser work but curl doesn’t?
Browser has a different TLS library and certificate store than curl. The team that uses openssl s_client (which uses the system OpenSSL) for testing has more accurate diagnosis.
Is the cert self-signed?
openssl s_client ... | openssl x509 -noout -issuer -subject shows the issuer. If issuer = subject, it’s self-signed.
Can I disable TLS verification for testing?
Yes - curl -k or wget --no-check-certificate. NEVER use in production. The team that tests with -k verifies the cert is the problem; then fixes the actual cert.
How long does a TLS handshake take?
1 round trip on modern networks (~50-100ms). TLS 1.3 is 1-RTT (vs TLS 1.2’s 2-RTT). Resumption is 0-RTT or 1-RTT.
If you are sizing the infrastructure for the kind of project this post covers, the RunxBuild hosting calculator is the right place to model the line items. The compute, the memory, the storage, the bandwidth, the database - each one is a separate number, and the team’s mental model for the platform is the sum of those numbers. The RunxBuild dashboard is where the team sees the actual usage in one place.
Useful related references: