# Fix Nginx SSL Handshake Failed with TLS 1.2 Client Browsers

When clients report SSL handshake failures to your Nginx server, the error log shows entries like this:

bash
2026/04/08 11:42:33 [info] 2341#2341: *34567 SSL_do_handshake() failed (SSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:SSL alert number 40) while SSL handshaking, client: 192.0.2.50, server: 0.0.0.0:443

This specific alert -- sslv3 alert handshake failure -- means the client and server could not agree on a common TLS protocol version or cipher suite. The client sends its list of supported ciphers, the server responds with its own, and if there is no overlap, the handshake fails.

Diagnosing the Mismatch

Use OpenSSL to test which protocols and ciphers your Nginx accepts:

bash
echo | openssl s_client -connect example.com:443 -tls1_2 2>&1 | grep -E "Protocol|Cipher"

If the command fails with handshake failure, Nginx is not configured to accept the client's cipher preferences. Test all protocol versions:

bash
echo | openssl s_client -connect example.com:443 -tls1 2>&1 | grep "Protocol"
echo | openssl s_client -connect example.com:443 -tls1_1 2>&1 | grep "Protocol"
echo | openssl s_client -connect example.com:443 -tls1_2 2>&1 | grep "Protocol"
echo | openssl s_client -connect example.com:443 -tls1_3 2>&1 | grep "Protocol"

Common Cause 1: TLS 1.2 Disabled

Check your Nginx SSL configuration:

bash
grep ssl_protocols /etc/nginx/nginx.conf

If you see only TLSv1.3, TLS 1.2 clients cannot connect. Fix it:

nginx
ssl_protocols TLSv1.2 TLSv1.3;

Many organizations still have clients that only support TLS 1.2: older Android devices (pre-5.0), Windows 7 without updates, legacy enterprise systems, and some IoT devices.

Common Cause 2: Cipher Suite Mismatch

The ssl_ciphers directive may be too restrictive. A balanced configuration that supports TLS 1.2 clients while maintaining security:

nginx
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;

Setting ssl_prefer_server_ciphers off allows the client to choose its preferred cipher from the intersection of both lists, which reduces handshake failures.

Common Cause 3: Missing Intermediate Certificate

If the server certificate chain is incomplete, clients cannot validate the handshake. Test:

bash
echo | openssl s_client -connect example.com:443 -showcerts 2>&1 | grep "Certificate chain" -A 20

You should see the full chain: leaf certificate, intermediate(s), and root. If the intermediate is missing, combine the certificates:

bash
cat /etc/letsencrypt/live/example.com/cert.pem /etc/letsencrypt/live/example.com/chain.pem > /etc/letsencrypt/live/example.com/fullchain.pem

Then update Nginx:

nginx
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

ECDSA vs RSA Certificate Issue

If you are using an ECDSA certificate but some clients only support RSA key exchange, you may need to serve both:

```nginx server { listen 443 ssl; server_name example.com;

ssl_certificate /etc/ssl/certs/example.com.rsa.pem; ssl_certificate_key /etc/ssl/private/example.com.rsa.key;

ssl_certificate /etc/ssl/certs/example.com.ecdsa.pem; ssl_certificate_key /etc/ssl/private/example.com.ecdsa.key;

ssl_protocols TLSv1.2 TLSv1.3; } ```

Nginx will negotiate the appropriate certificate based on the client's capabilities.

Testing the Fix

After reloading Nginx:

bash
sudo nginx -t && sudo systemctl reload nginx

Run the full test suite:

bash
echo | openssl s_client -connect example.com:443 -tls1_2 2>&1 | grep "Verify return code"
nmap --script ssl-enum-ciphers -p 443 example.com
./testssl.sh https://example.com

The testssl.sh tool will show you exactly which clients (browsers, operating systems) can and cannot connect to your server, along with the grade for your SSL configuration.