SSL/TLS Troubleshooting Guide - Practical Solutions
It’s 3 AM. PagerDuty fires. Your checkout page is down. You SSH in, check Nginx — it’s running fine. Then you notice it: NET::ERR_CERT_DATE_INVALID. The certificate expired six hours ago. Everyone’s been asleep. Sales have been zero for six hours.
Sound familiar? Certificate problems are among the most common causes of unexpected downtime, and they’re almost entirely preventable. This guide is the field manual I wish I’d had: practical diagnosis, concrete fixes, and enough context to understand why these problems happen — not just how to patch them this time.
If you’re setting up a fresh Nginx server and want to get SSL right from the start, see our Nginx SSL installation guide first. But if you’re already in a production incident, read on.
Diagnostic Tools
OpenSSL - Essential Tool
# Check certificate
openssl s_client -connect example.com:443 -showcerts
# Expiration date
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# Certificate details
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -text
Testssl.sh - Comprehensive Analysis
# Installation
git clone https://github.com/drwetter/testssl.sh.git
cd testssl.sh
# Test
./testssl.sh example.com
Problem 1: Expired Certificate
Symptoms: NET::ERR_CERT_DATE_INVALID
Diagnosis:
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
Solution:
# Let's Encrypt
certbot renew
systemctl reload nginx
# Automatic renewal
echo "0 3 * * * certbot renew --quiet" | crontab -
Problem 2: Incomplete Certificate Chain
Symptoms: unable to get local issuer certificate
Diagnosis:
openssl s_client -connect example.com:443 -showcerts
# You should see certificate + intermediate + root
Nginx Solution:
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # fullchain!
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
Manual fullchain creation:
cat your-cert.crt intermediate.crt > fullchain.crt
Problem 3: Hostname Mismatch
Symptoms: NET::ERR_CERT_COMMON_NAME_INVALID
Diagnosis:
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -text | grep -A2 "Subject Alternative Name"
Solution:
# Add all domains to certificate
certbot certonly --nginx -d example.com -d www.example.com -d api.example.com
Problem 4: Mixed Content
Symptoms: Mixed Content Warning in browser console
Diagnosis:
curl -s https://example.com | grep -o 'http://[^"]*'
Nginx Solution:
add_header Content-Security-Policy "upgrade-insecure-requests;" always;
Code Solution:
<!-- Instead of http:// use // -->
<img src="//example.com/image.jpg">
Problem 5: Self-Signed Certificate
Symptoms: NET::ERR_CERT_AUTHORITY_INVALID
Diagnosis:
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -issuer -subject
# issuer == subject? It's self-signed!
Production Solution:
# Replace with Let's Encrypt
certbot certonly --nginx -d example.com
Problem 6: TLS Version Mismatch
Symptoms: ERR_SSL_VERSION_OR_CIPHER_MISMATCH
Diagnosis:
nmap --script ssl-enum-ciphers -p 443 example.com
Nginx Solution:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
Problem 7: Port 443 Blocked
Symptoms: Timeout, connection refused
Diagnosis:
# Test connection
telnet example.com 443
nc -zv example.com 443
# Check firewall
sudo iptables -L -n | grep 443
Solution:
# Open port 443
sudo ufw allow 443/tcp
sudo firewall-cmd --permanent --add-service=https
Problem 8: SNI Issues
Symptoms: Wrong certificate for domain
Diagnosis:
# Test with SNI
openssl s_client -connect example.com:443 -servername example.com
# Test without SNI
openssl s_client -connect example.com:443 -noservername
Nginx Solution:
# Each domain needs its own server block
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /path/to/example.com.crt;
ssl_certificate_key /path/to/example.com.key;
}
Problem 9: OCSP Stapling Issues
Diagnosis:
openssl s_client -connect example.com:443 -status 2>/dev/null | grep -A 17 "OCSP response"
Nginx Solution:
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 10s;
Problem 10: Certificate Revoked
Symptoms: NET::ERR_CERT_REVOKED
Diagnosis:
openssl ocsp -issuer chain.pem -cert cert.pem -url http://ocsp.example.com
Solution: Certificate has been revoked - you must obtain a new one:
# Generate new key!
openssl genrsa -out new-key.pem 2048
# Get new certificate
certbot certonly --nginx -d example.com --force-renewal
Quick Troubleshooting Checklist
- Expiration date - has the certificate expired?
- Certificate chain - are you using fullchain?
- Hostname - is the domain in SAN?
- Issuer - is the CA trusted?
- TLS version - do you support TLS 1.2+?
- Port 443 - is it open?
- DNS - does the domain point to the correct IP?
- Server logs - what do error logs say?
Online Tools
- SSL Labs Server Test - comprehensive analysis
- Why No Padlock? - mixed content debugging
- SSL Checker - quick certificate test
Monitoring and Prevention
The best strategy is to prevent problems. A good Prometheus and Grafana monitoring setup for SSL certificates will catch expiring certificates weeks before they become incidents. But if you want a quick script to get started right now:
# Monitoring script
#!/bin/bash
DOMAIN="example.com"
DAYS_LEFT=$(echo | openssl s_client -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -checkend $((30*86400)))
if [ $? -ne 0 ]; then
echo "Certificate expires soon!" | mail -s "SSL Alert" admin@example.com
fi
Or use a dedicated tool like CrtMgr, which automatically monitors certificates and sends alerts before expiration. Either way, the goal is the same: never be surprised by an expired certificate again.
Most SSL/TLS problems collapse into a handful of root causes: expired certificates (automate renewals — seriously, just do it), incomplete chain (always use fullchain.pem), hostname mismatches (add all domains to SAN upfront), and mixed content (the CSP header is your friend). None of these are hard to fix; they’re just easy to overlook under pressure.
The real goal is to stop troubleshooting and start preventing. Methodical monitoring beats reactive firefighting every time.
Good troubleshooting = methodical approach + right tools. Great operations = catching problems before they become incidents.