# 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.When a client connects to Nginx over HTTPS, a handshake occurs:
- 2.Client announces supported TLS versions and cipher suites
- 3.Server presents its certificate
- 4.Client verifies the certificate chain
- 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.Test SSL configuration:
- 2.```bash
- 3.sudo nginx -t
- 4.
` - 5.Test with openssl:
- 6.```bash
- 7.openssl s_client -connect example.com:443 -servername example.com
- 8.
` - 9.Online SSL test:
- 10.Use SSL Labs: https://www.ssllabs.com/ssltest/
- 11.Reload Nginx:
- 12.```bash
- 13.sudo systemctl reload nginx
- 14.
`
Quick Reference
| Error | Cause | Fix |
|---|---|---|
bad certificate | Incomplete chain | Use fullchain.pem |
no protocols available | TLS version mismatch | Enable TLSv1.2 and TLSv1.3 |
certificate verify failed | Expired or wrong cert | Renew or correct certificate |
handshake failure | Cipher mismatch | Update cipher suite |
unknown CA | Missing intermediate | Bundle all certificates |
| Connection reset | HTTP on HTTPS port | Add 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.