Servers and proxies

SSL handshake failed: find the cause by client and server

By · Last verified

Error messageSSL handshake failed

"SSL handshake failed" means the client and the server could not finish setting up a TLS connection, so nothing was encrypted and no request was sent. It is a generic message: Cloudflare shows it as Error 525, and browsers and apps use more specific codes. Find who reported it and at which step, then use the tables below.

Where a handshake can fail

A TLS handshake runs through a few steps; each has typical failures. The TLS handshake walk-through explains the steps in detail.

Step What happens Typical failure
1. Connect TCP connection to port 443 Port closed, firewall, or the port speaks plain HTTP
2. ClientHello Client offers versions, cipher suites, SNI hostname No common version or cipher; no certificate for that name
3. Server certificate Server sends its certificate chain Expired, untrusted, wrong name, missing intermediate
4. Client certificate (optional) Server asks the client to authenticate No or rejected client certificate
5. Finished Keys confirmed, encryption starts Interference by proxies or antivirus, resets

The side that notices the problem usually sends an alert. RFC 9846 defines, among others, handshake_failure (40), protocol_version (70), unrecognized_name (112) and certificate_required (116).

Client-side messages

Browsers

Message Step Page
ERR_SSL_PROTOCOL_ERROR 1–2 ERR_SSL_PROTOCOL_ERROR
ERR_SSL_VERSION_OR_CIPHER_MISMATCH 2 version or cipher mismatch
ERR_SSL_UNRECOGNIZED_NAME_ALERT 2 unrecognized name alert
SSL_ERROR_HANDSHAKE_FAILURE_ALERT 2 or 4 Firefox handshake failure alert
SSL_ERROR_NO_CYPHER_OVERLAP 2 no cypher overlap
SSL_ERROR_RX_RECORD_TOO_LONG 1 record too long
PR_END_OF_FILE_ERROR 1–5 PR_END_OF_FILE_ERROR
ERR_BAD_SSL_CLIENT_AUTH_CERT 4 client certificate rejected
NET::ERR_CERT_AUTHORITY_INVALID, DLG_FLAGS_INVALID_CA 3 untrusted issuer, IE mode
"Safari can't establish a secure connection to the server" 1–2 Safari

Certificate errors at step 3 in browsers usually show an interstitial such as Your connection is not private rather than a plain handshake error.

Apps, languages and command-line tools

Message Page
NSURLErrorDomain Code=-1200 iOS secure connection failed
NSURLErrorDomain Code=-1202 iOS untrusted certificate
Flutter HandshakeException ... CERTIFICATE_VERIFY_FAILED Flutter
Android Trust anchor for certification path not found Android trust anchor
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure Java handshake_failure
Python SSLEOFError / WRONG_VERSION_NUMBER EOF in violation of protocol, wrong version number
curl: (35) curl 35
curl: (60) curl 60

In OpenSSL-based tools a handshake_failure alert from the server appears as sslv3 alert handshake failure (OpenSSL 1.1.1 and 3.0); newer OpenSSL versions word it as ssl/tls alert handshake failure.

Server, proxy and CDN messages

Where Message Page
Cloudflare Error 525: SSL handshake failed Cloudflare 525
Cloudflare Error 526: invalid SSL certificate Cloudflare 526
nginx SSL_do_handshake() failed, no shared cipher, peer closed connection in SSL handshake Diagnose from outside, below
Apache AH02572, AH01909 at startup AH02572, AH01909
IIS A specified logon session does not exist IIS binding
Windows Schannel events Schannel errors

nginx logs many handshake failures caused by clients, including no shared cipher, unsupported protocol versions and received alerts, at info level. With the default error_log ... error; you never see them. Raise the level temporarily:

error_log /var/log/nginx/error.log info;

Diagnose from outside

Run these from a machine outside the failing network:

openssl s_client -connect example.com:443 -servername example.com -brief </dev/null
openssl s_client -connect example.com:443 -servername example.com -tls1_2 -brief </dev/null
openssl s_client -connect example.com:443 -servername example.com -tls1_3 -brief </dev/null
  • wrong version number or packet length too long: the port is not speaking TLS (step 1).
  • An alert such as handshake failure or protocol version: version, cipher or client certificate problem (steps 2 and 4).
  • The handshake completes but Verify return code is not 0: certificate problem (step 3).
  • Works without -servername but not with it, or the reverse: SNI configuration.

The SSL checker runs the same tests and shows protocol support and the chain. To fix protocol and cipher settings, start from the TLS config generator; for chain problems see missing intermediate certificate.

How to verify the fix

  1. All three openssl s_client commands above complete, or fail only for versions you intentionally disabled.
  2. The client that originally failed connects, on the same network.
  3. With info-level logging enabled for a while, the server log shows no new handshake errors from real clients; then restore the normal log level.

FAQ

Is "SSL handshake failed" a problem with my certificate?

Sometimes. Certificate problems are only one group of causes; many handshake failures happen before the certificate is checked, such as a port without TLS or no common cipher suite. The exact error code tells you which group you are in.

Should it still be called SSL?

Technically every modern connection uses TLS, usually 1.2 or 1.3; SSL 3.0 and earlier are long obsolete. Error messages and libraries keep the old name, which is why "SSL handshake failed" still appears everywhere.

The handshake fails only for some visitors. Where do I start?

Compare what differs: old devices (missing roots, no TLS 1.2), corporate networks or antivirus that intercept TLS, and clients that do not send SNI. Enable info-level logging on the server temporarily and match timestamps.

What is the quickest single test?

openssl s_client -connect example.com:443 -servername example.com -brief from a machine outside your network, plus the SSL checker. If both succeed, the problem is on the failing client's side or network.

Sources

  1. RFC 9846: The Transport Layer Security (TLS) Protocol Version 1.3
  2. Chromium net_error_list.h
  3. NSS SSLerrs.h (Firefox TLS errors)
  4. nginx source: ngx_event_openssl.c
  5. Cloudflare: Error 525