SSL handshake failed: what it means and how to fix it
Kofi Agyem, Founder and CEO, CloudzyroPublished · 5 min read
On this page
An SSL handshake failure means client and server couldn't agree how to secure the connection. Causes, Cloudflare error 525, and how to test with openssl.
1. The short answer
An SSL handshake failure means the client and the server couldn't agree on how to secure the connection, so it was closed before any page was sent. The handshake is where they pick a TLS version and cipher, and where the server proves who it is with its certificate. If they share no version or cipher, the certificate can't be used, or something interferes, the handshake fails.
You'll meet it as Cloudflare's Error 525: SSL handshake failed, as browser errors like ERR_SSL_VERSION_OR_CIPHER_MISMATCH, or in tools and code as handshake failure. In nearly every case the fix is on the server: modern TLS versions, a valid certificate for the name, and support for SNI.
2. What happens in a TLS handshake
- Client hello. The client says which TLS versions and ciphers it supports, which site it wants (SNI, the server name indication), and sends key material.
- Server hello. The server picks a version and cipher both support, and sends its own key material.
- Certificate. The server sends its certificate chain and proves it holds the private key.
- Finished. Both sides confirm. With TLS 1.3 this all takes one round trip; then encrypted HTTP begins.
A failure at any step aborts the connection. The side that gives up usually sends an alert, such as handshake_failure (no shared settings) or protocol_version (no shared version), which is the text you see in logs.
3. Where you'll see it
- Cloudflare: Error 525, SSL handshake failed, between Cloudflare and the site's server. Its cousin, Error 526, means the origin's certificate is invalid under Full (strict).
- Chrome and Edge: This site can't provide a secure connection, with ERR_SSL_VERSION_OR_CIPHER_MISMATCH or ERR_SSL_PROTOCOL_ERROR.
- Firefox: Secure Connection Failed, with codes like SSL_ERROR_NO_CYPHER_OVERLAP or SSL_ERROR_UNSUPPORTED_VERSION.
- curl:
(35) ... sslv3 alert handshake failureortlsv1 alert protocol version. - Code: Java's
SSLHandshakeException, Python'sSSLErrorwithHANDSHAKE_FAILURE, Node'sEPROTO.
4. What causes it
- No shared TLS version. The server offers only TLS 1.0 or 1.1, which current clients refuse, or only TLS 1.3 for a client that's too old for it.
- No shared cipher. The server allows only ciphers the client doesn't have, often after over-zealous hardening.
- SNI problems. The client doesn't send the site's name, or the server has no certificate for it, and aborts.
- No certificate configured on the HTTPS port, or a certificate whose key doesn't match.
- A client certificate is required (mutual TLS) and the client didn't send one.
- Something in the middle: antivirus, a company proxy, a captive portal or a firewall that inspects TLS.
- On Cloudflare (525): the origin isn't listening on 443, has no certificate, doesn't support SNI or shares no cipher with Cloudflare, while the zone's SSL mode is Full or Full (strict).
- Wrong clock on either side, which breaks certificate validation.
5. Fixes for visitors, quickest first
- Check your date and time are set automatically.
- Update your browser and operating system. Very old systems may not support TLS 1.2 or trust today's root certificates.
- Try another network. On public Wi-Fi, sign in through its page first.
- Pause antivirus HTTPS scanning or your VPN briefly to test.
- If it fails everywhere, it's the site. Let the owner know.
6. Fixes for site owners
- Test the handshake with
openssl s_client, forcing each version (below). - Enable TLS 1.2 and 1.3, and disable SSL 3.0, TLS 1.0 and 1.1. Use a current recommended cipher list rather than a hand-trimmed one.
- Serve a valid certificate for every name, with its full chain and the matching private key.
- For mutual TLS, make sure clients that should connect have a certificate, and that public pages don't require one.
- For Cloudflare 525: make sure the origin listens on 443 with a certificate and supports SNI. Then use Full (strict) with a valid origin certificate. Don't switch to Flexible to hide it: that sends traffic to your server unencrypted and often causes redirect loops.
- For API clients, update the runtime (Java, Python, OpenSSL) so it supports TLS 1.2 or later and current root certificates.
On Cloudzyro Sites & Apps, TLS versions, ciphers and certificates are managed for you, and every site sits behind Cloudflare. If your DNS is also at Cloudflare, set your domain's records to DNS only (gray cloud), not Proxied. CAA records, if any, must allow letsencrypt.org, pki.goog and ssl.com. The site's Domains tab and the domain troubleshooting guide say what's needed.
7. How to check
# Try a normal handshake, sending SNI like a browser does
openssl s_client -connect www.example.com:443 -servername www.example.com
# Force one version at a time
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1_3
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1_2
# Test an origin behind Cloudflare directly (use the origin's IP)
curl -v --resolve www.example.com:443:203.0.113.10 https://www.example.com/
# List the versions and ciphers a server accepts (needs nmap)
nmap --script ssl-enum-ciphers -p 443 www.example.comIf -tls1_2 and -tls1_3 both fail with handshake failure or protocol version, the server's TLS settings are wrong. If both fail only when -servername is missing, the server depends on SNI, which is normal for modern clients. The --resolve test bypasses Cloudflare, so it shows whether the origin can complete a handshake on its own, which is exactly what error 525 is about.