← Debugging Guides

阅读中文版本

TLS handshake failure — Certificate, SNI, and Cipher Mismatch

Diagnose TLS handshake failures with openssl s_client and curl verbose output, checking certificate chains, SNI virtual hosts, protocol versions, and client trust.

intermediatedebuggingtlsopenssl

Symptoms

  • curl: (35) error:0A000410:SSL routines::sslv3 alert handshake failure
  • SSL_ERROR_SSL or ERR_SSL_VERSION_OR_CIPHER_MISMATCH in the browser
  • The handshake fails before any HTTP response is received
  • Some clients connect while others fail against the same host

Possible causes

  • The server sends an incomplete certificate chain and the client cannot build a path to a trusted root
  • The virtual host serves the wrong certificate because SNI is missing or mismatched
  • Client and server have no protocol version or cipher suite in common
  • The certificate is expired, not yet valid, or the system clock is wrong
  • The server requires a client certificate that the client did not present

Diagnosis steps

Perform a verbose handshake with SNI

openssl s_client -connect example.com:443 -servername example.com -tls1_2 </dev/null 2>&1 | grep -E 'subject=|issuer=|Verify return code|Protocol|Cipher|alert'

Interpretation

Verify return code 0 means the chain validated. A nonzero code names the chain problem. An alert during the handshake indicates protocol or cipher disagreement.

Check the certificate dates and names

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

Interpretation

Confirm the SAN covers the hostname and that the validity window includes now. An expired or wrong-name certificate produces a verification error, not a cipher alert.

Repeat as an HTTP client

curl -vI https://example.com 2>&1 | grep -E 'TLS|SSL|subject|issuer|HTTP/'

Interpretation

curl shows the negotiated TLS version, the certificate chain it received, and the exact verification error, which is closer to what the application sees.

Enumerate supported protocols and ciphers

nmap --script ssl-enum-ciphers -p 443 example.com

Interpretation

The report lists TLS versions and cipher suites the server accepts. An old client failing while modern browsers work usually means a removed protocol or cipher.

Root cause

A TLS handshake fails when the peers cannot agree on a protocol and cipher, when the certificate cannot be validated, or when the server selects a certificate for the wrong name. Validation includes the chain, expiry, hostname, and any client-certificate requirement. Because the failure happens before HTTP, the application log may show nothing, and the client error is the primary evidence.

Fix

Serve the full chain including intermediates, configure SNI virtual hosts with the correct certificate, and enable protocol versions and ciphers that both sides support. Renew expired certificates and fix clock skew on the client. For mTLS, distribute the client certificate and trust the issuing CA on the server. Do not re-enable obsolete TLS 1.0 or weak ciphers to accommodate one outdated client.

Risk note

Disabling certificate verification or using curl -k hides the actual trust failure and is unsafe in production. Installing a self-signed certificate as a trusted root affects every application on the host. Broad cipher compatibility can reintroduce known weaknesses.

Verify the fix

Re-run openssl s_client and confirm Verify return code 0 and the expected protocol and cipher. Test with curl from the failing client and from a clean host, then confirm the application request succeeds without disabling verification.

The failure happens before HTTP

curl: (35) error:0A000410:SSL routines::sslv3 alert handshake failure

No HTTP status exists because the TLS layer never completed. The client error distinguishes a verification failure (untrusted chain, wrong name) from an alert (no shared protocol or cipher).

Test the handshake directly

openssl s_client -connect example.com:443 -servername example.com -tls1_2 </dev/null
curl -vI https://example.com
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

-servername is essential: without SNI, a shared load balancer may return the default certificate. Verify return code: 0 (ok) means the chain validated on this host.

Fix the specific layer

  • Incomplete chain: configure ssl_certificate with the leaf plus intermediate bundle.
  • Wrong certificate: check the SNI virtual host mapping and default server block.
  • Protocol mismatch: enable TLS 1.2 and 1.3 and remove obsolete ciphers only after confirming client support.
  • Expired or not yet valid: renew and check the client clock.
  • mTLS: present the client certificate and trust its CA on the server.
ssl_certificate     /etc/ssl/example/fullchain.pem;
ssl_certificate_key /etc/ssl/example/privkey.pem;
ssl_protocols       TLSv1.2 TLSv1.3;

Verify

Re-run openssl s_client and confirm Verify return code: 0 (ok) with the expected protocol. Test from the originally failing client and from a clean network, then send a real request with certificate verification enabled. If only one client fails, compare its trust store and clock rather than weakening the server configuration.