How To Guide

How to Fix SSL/TLS Handshake Failure: Complete Debug Guide

Diagnose and fix SSL handshake failures. Debug protocol mismatches, cipher issues, cert chain problems, and SNI errors with openssl and curl.

An SSL/TLS handshake failure means the client and server could not agree on how to establish an encrypted session. The connection is aborted before any application data is exchanged. Because the failure can originate from either side and be triggered by protocols, ciphers, certificates, or clocks, methodical debugging is essential. This guide walks through the eight most common causes and shows how to isolate each with openssl, curl, and Wireshark.

Step 1: Capture the exact error

Start by reproducing the failure with verbose output. In a browser, open DevTools and check the Security tab; note the ERR_SSL_* code. From the command line, use:

curl -v https://example.com  # add `--trace-ascii /dev/stderr` for byte-level detail

Look for lines beginning with *. Common signals include SSL_ERROR_NO_CYPHER_OVERLAP, alert handshake failure, unable to get local issuer certificate, or tlsv1 alert protocol version. The alert number in the TLS record indicates which side rejected: alert 40 (handshake_failure), 42 (bad_certificate), 46 (certificate_unknown), 70 (protocol_version), or 112 (unrecognized_name for SNI issues).

Step 2: Test protocol version support

A frequent cause is protocol mismatch: the client offers only TLS 1.2 while the server disabled everything below 1.3, or vice versa. Test each version explicitly:

openssl s_client -connect example.com:443 -servername example.com -tls1_3
openssl s_client -connect example.com:443 -servername example.com -tls1_2
openssl s_client -connect example.com:443 -servername example.com -tls1_1

If -tls1_2 succeeds but -tls1_3 fails (or the reverse), you have found a protocol-version issue. Update the client library, enable the missing version on the server, or align both sides on a shared version. Note that TLS 1.0 and 1.1 are deprecated and disabled by default in modern browsers and OpenSSL 3.x.

Step 3: Check cipher suite overlap

Even with matching protocol versions, the two sides must share at least one cipher suite. List what the server offers:

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

Compare against your client's supported list (openssl ciphers -v). If the server only exposes AEAD ciphers (AES-GCM, ChaCha20-Poly1305) and your client is an older embedded device that only speaks CBC-mode suites, the handshake will fail with alert 40. Force a specific cipher to test:

openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES128-GCM-SHA256'

Step 4: Validate the certificate chain

Expired, self-signed, or hostname-mismatched certificates cause the client to abort. Inspect the presented chain:

openssl s_client -connect example.com:443 -servername example.com -showcerts

Verify the notAfter date, the CN/SAN entries, and that intermediate certificates are included. A very common misconfiguration is a server sending only its leaf certificate without the intermediate. Browsers on desktop often cache intermediates and succeed, but mobile clients, curl, and Java apps fail. Fix by bundling the full chain in your server configuration.

Step 5: Verify SNI is being sent correctly

Server Name Indication (SNI) tells the server which virtual host you want. Without it, the server may present the default cert or refuse the connection with alert 112. Always include -servername in openssl tests and confirm curl is sending the correct Host header. If a load balancer or CDN sits in front, ensure the SNI matches the certificate installed there, not the origin.

Step 6: Rule out client certificates, OCSP, and clock drift

Three subtler causes remain. First, if the server requires mutual TLS, it sends a CertificateRequest and the client must respond with a certificate signed by an accepted CA. Check the openssl output for Acceptable client certificate CA names. Second, OCSP stapling failures can trigger revocation errors. Test with:

openssl s_client -connect example.com:443 -status

Look for OCSP Response Status: successful. Third, if the client's system clock drifts more than a few minutes, valid certificates appear expired or not-yet-valid. Sync with NTP: timedatectl status on Linux, w32tm /query /status on Windows.

Step 7: Packet-level analysis with Wireshark

When openssl and curl leave you without answers, capture packets. Filter with tcp.port == 443 and inspect the Client Hello and Server Hello records. You can see the exact cipher list offered, extensions sent (including SNI), the server's selected version, and any Alert record with a specific reason. This is the most authoritative source of truth and often reveals middleboxes stripping extensions or downgrading the connection.

Quick reference table

SymptomLikely causeFix
alert 70 protocol_versionTLS version mismatchEnable common version on both sides
alert 40 handshake_failureNo shared cipherUpdate cipher suites
unable to get local issuerMissing intermediateBundle full chain
alert 112 unrecognized_nameSNI mismatchSend correct servername
certificate has expiredReal expiry or clock driftRenew cert or sync NTP

Work through these steps sequentially and you will resolve nearly every handshake failure you encounter.

ssl handshake failure tls handshake error openssl s_client debug cipher suite mismatch sni mismatch certificate chain missing tls 1.3 debug handshake_failure alert

Explore 300+ Free Tools

Utilko has tools for developers, writers, designers, students, and everyday users — all free, all browser-based.