Error message
SSL routines:ssl3_read_bytes:ssl/tls alert handshake failure
Also printed as
- SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
- SSL alert number 40
OpenSSL 3.3 and later print "ssl/tls alert handshake failure", earlier versions "sslv3 alert handshake failure" (same code 0A000410; 1.1.1 used 14094410). The line ends with the source location and "SSL alert number 40".
OpenSSL (client side) — TLS alert 40 (handshake_failure) received from the server
What it means
The server sent TLS alert 40 (handshake_failure) and closed the connection. It is a generic refusal: the server found no acceptable cipher suite, key-exchange group or signature algorithm in the ClientHello, required a client certificate, or rejected the requested server name. The client cannot learn the reason from the alert; the server's log has it.
Why it happens
No common cipher suite
commonLegacy servers limited to RSA key exchange, 3DES or RC4 suites; or strict servers allowing a handful of suites an old client lacks.
A client certificate is required (TLS 1.2)
commonWith TLS 1.2, many servers answer a missing or unacceptable client certificate with handshake_failure; TLS 1.3 servers use certificate_required (alert 116) instead.
Missing or unknown SNI
occasionalopenssl s_client sends SNI only when the host is a name or -servername is given; CDNs and multi-tenant proxies abort handshakes without a known name.
No common group or signature algorithm
rareServers restricted to a single group (for example a post-quantum hybrid) or to RSA-PSS signatures can fail against clients that offer neither.
How to fix it
- 1.
Read the server's log
The server records the actual reason, e.g. "no shared cipher" or "peer did not return a certificate".
bash # The server knows why; with openssl s_server or nginx the log says e.g. # ...:tls_post_process_client_hello:no shared cipher # ...:tls_process_client_certificate:peer did not return a certificate sudo tail -n 50 /var/log/nginx/error.log | grep -i ssl - 2.
Narrow it down from the client
Vary SNI, the protocol version, the cipher list and the presence of a client certificate; the variant that succeeds identifies the missing piece.
bash h=api.example.com echo | openssl s_client -connect $h:443 -servername $h -brief # with SNI echo | openssl s_client -connect $h:443 -servername $h -tls1_2 -cipher 'ALL:@SECLEVEL=0' -brief echo | openssl s_client -connect $h:443 -servername $h -cert client.pem -key client.key -brief # mTLS - 3.
Server owners: offer current suites
TLS 1.3 plus TLS 1.2 with ECDHE and AEAD suites works with every maintained client.
text # nginx: modern suites for TLS 1.2 plus TLS 1.3 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
Related errors
SSL routines:tls_post_process_client_hello:no shared cipherOpenSSL (server side: nginx, Apache, Postfix, openssl s_server)SSL routines:tls_process_client_certificate:peer did not return a certificateOpenSSL (server side, mutual TLS)curl: (35) TLS connect error: error:0A000410:SSL routines::ssl/tls alert handsh…curl (OpenSSL backend)[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] ssl/tls alert handshake failurePython ssl module (OpenSSL)