# Nginx SSL Protocol Error

SSL/TLS errors in Nginx appear as browser warnings, connection failures, or cryptic protocol errors. Users see "This site can't provide a secure connection" or "SSL handshake failed." The causes range from missing certificate chains to protocol version mismatches.

Understanding SSL/TLS Handshake Errors

  1. 1.When a client connects to Nginx over HTTPS, a handshake occurs:
  2. 2.Client announces supported TLS versions and cipher suites
  3. 3.Server presents its certificate
  4. 4.Client verifies the certificate chain
  5. 5.Both negotiate encryption parameters

Any step failing produces an SSL protocol error.

Check the Nginx error log: ``bash tail -f /var/log/nginx/error.log

Common error messages: `` SSL_do_handshake() failed (SSL: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate) peer closed connection in SSL handshake while SSL handshaking SSL: error:1416A0BF:SSL routines:tls_process_client_hello:no protocols available

Common Cause 1: Incomplete Certificate Chain

The server certificate alone isn't enough. Intermediate certificates must be included for the chain to validate.

Diagnosis: ```bash # Check certificate chain openssl s_client -connect localhost:443 -showcerts

# Or check online curl -vI https://example.com 2>&1 | grep -A 5 "SSL connection" ```

The problem: ``nginx ssl_certificate /etc/nginx/ssl/server.crt; # Only server cert ssl_certificate_key /etc/nginx/ssl/server.key;

Solution: Bundle certificates: ``bash # Concatenate server cert with intermediate cat server.crt intermediate.crt > fullchain.crt

Or download the full chain from your CA (Let's Encrypt provides this): ``bash # Let's Encrypt stores fullchain by default ls /etc/letsencrypt/live/example.com/ # fullchain.pem - server + intermediates # cert.pem - server only # chain.pem - intermediates only

Correct Nginx config: ```nginx server { listen 443 ssl; server_name example.com;

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

Common Cause 2: TLS Version Mismatch

The client and server have no TLS versions in common.

The problem: ``nginx # Server only supports TLS 1.0 ssl_protocols TLSv1;

Modern browsers reject TLS 1.0 and 1.1.

Or the opposite: ``nginx # Server requires TLS 1.3 ssl_protocols TLSv1.3;

Older clients don't support TLS 1.3.

Solution: Enable modern protocols: ``nginx ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

Test TLS versions: ```bash # Test TLS 1.2 openssl s_client -connect example.com:443 -tls1_2

# Test TLS 1.3 openssl s_client -connect example.com:443 -tls1_3 ```

Common Cause 3: Certificate and Key Mismatch

The certificate and private key don't correspond to each other.

Diagnosis: ```bash # Get certificate modulus hash openssl x509 -noout -modulus -in server.crt | openssl md5

# Get key modulus hash openssl rsa -noout -modulus -in server.key | openssl md5 ```

If the hashes don't match, you have the wrong key.

Solution: Find the correct key, or regenerate the certificate: ```bash # Generate new key and CSR openssl genrsa -out server.key 2048 openssl req -new -key server.key -out server.csr

# Get certificate reissued ```

Common Cause 4: Expired Certificate

Certificates have validity periods. Expired certs cause immediate rejection.

Diagnosis: ```bash # Check certificate dates openssl x509 -enddate -noout -in /etc/nginx/ssl/server.crt

# Or check all sites certbot certificates # For Let's Encrypt ```

Solution: ```bash # Renew Let's Encrypt certificates sudo certbot renew

# Or for manual certificates, get a new one ```

Auto-renewal setup: ```bash # Test renewal sudo certbot renew --dry-run

# Add to cron sudo crontab -e # Add line: 0 12 * * * /usr/bin/certbot renew --quiet ```

Common Cause 5: Wrong Certificate for Domain

Certificate doesn't cover the requested domain name.

Diagnosis: ``bash # Check certificate SAN (Subject Alternative Names) openssl x509 -in server.crt -text -noout | grep -A1 "Subject Alternative Name"

Output shows covered domains: `` Subject Alternative Name: DNS:example.com, DNS:www.example.com

If you request api.example.com but cert only covers example.com, the handshake fails.

Solution:

Add the domain to certificate: ``bash # Let's Encrypt - add domain sudo certbot certonly --nginx -d example.com -d www.example.com -d api.example.com

Or use a wildcard certificate: ``bash sudo certbot certonly --manual --preferred-challenges dns -d '*.example.com' -d example.com

Common Cause 6: Cipher Suite Incompatibility

Client and server have no cipher suites in common.

Diagnosis: ```bash # List server's ciphers nmap --script ssl-enum-ciphers -p 443 example.com

# Test with specific cipher openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES128-GCM-SHA256' ```

The problem: ``nginx ssl_ciphers HIGH:!aNULL:!MD5; # Too restrictive or outdated

Solution: Modern cipher suite: ``nginx 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; ssl_prefer_server_ciphers off;

Common Cause 7: HTTP on HTTPS Port

HTTP traffic sent to HTTPS port.

The problem: ``nginx server { listen 443; # Missing 'ssl' server_name example.com; }

Solution: ```nginx server { listen 443 ssl; # Must include 'ssl' server_name example.com;

ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; } ```

Common Cause 8: SNI Issues

Server Name Indication (SNI) is required when hosting multiple SSL certificates on one IP.

Problem: Some old clients don't support SNI, causing the wrong certificate to be served.

Check SNI: ```bash # Without SNI openssl s_client -connect example.com:443

# With SNI openssl s_client -connect example.com:443 -servername example.com ```

Verify correct certificate is returned: ``bash echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -subject

Common Cause 9: OCSP Stapling Issues

OCSP stapling can fail if the server can't reach the CA's OCSP responder.

The error: `` SSL: error:...:ocsp response decode error

Diagnosis: ``bash # Test OCSP openssl ocsp -issuer chain.pem -cert cert.pem -url http://ocsp.example.com -resp_text

Solution: ```nginx # Disable OCSP stapling temporarily ssl_stapling off;

# Or ensure resolver is configured ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; ```

Verification Steps

  1. 1.Test SSL configuration:
  2. 2.```bash
  3. 3.sudo nginx -t
  4. 4.`
  5. 5.Test with openssl:
  6. 6.```bash
  7. 7.openssl s_client -connect example.com:443 -servername example.com
  8. 8.`
  9. 9.Online SSL test:
  10. 10.Use SSL Labs: https://www.ssllabs.com/ssltest/
  11. 11.Reload Nginx:
  12. 12.```bash
  13. 13.sudo systemctl reload nginx
  14. 14.`

Quick Reference

ErrorCauseFix
bad certificateIncomplete chainUse fullchain.pem
no protocols availableTLS version mismatchEnable TLSv1.2 and TLSv1.3
certificate verify failedExpired or wrong certRenew or correct certificate
handshake failureCipher mismatchUpdate cipher suite
unknown CAMissing intermediateBundle all certificates
Connection resetHTTP on HTTPS portAdd ssl to listen directive

SSL errors require checking both ends: the server's configuration and the client's capabilities. Start with the certificate chain, then verify protocols and ciphers.