Introduction
When an SSL/TLS certificate expires, modern browsers display a full-page security warning that blocks users from accessing the site. Users see Your connection is not private (Chrome), Warning: Potential Security Risk Ahead (Firefox), or This connection is not private (Safari). Most non-technical users will not proceed past this warning, effectively making the site unavailable. Certificate expiration is one of the most common and easily preventable causes of website downtime.
Symptoms
- Browser shows full-page SSL warning before loading the site
curl -vI https://example.comshowsSSL certificate problem: certificate has expiredopenssl x509 -in cert.pem -noout -datesshowsnotAfterdate in the past- API clients and mobile apps fail with SSL verification errors
- Site works with HTTP but not HTTPS
Common Causes
- Certificate renewal process failed silently (ACME challenge error, DNS validation failure)
- Automated renewal (certbot cron) not configured or not running
- Certificate purchased for 1 year but renewal forgotten
- New certificate generated but not deployed to the web server
- Certificate authority changed and old cert expired before new one installed
Step-by-Step Fix
- 1.Verify the certificate is expired:
- 2.```bash
- 3.openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | \
- 4.openssl x509 -noout -dates -subject
- 5.# notAfter=Apr 01 00:00:00 2026 GMT (if this is in the past, it is expired)
- 6.
` - 7.Renew the certificate:
- 8.```bash
- 9.# For Let's Encrypt / Certbot:
- 10.sudo certbot renew --force-renewal
- 11.# If DNS challenge is needed:
- 12.sudo certbot certonly --manual --preferred-challenges dns -d example.com -d www.example.com
- 13.
` - 14.For manually purchased certificates, obtain a new one from the CA:
- 15.Log in to your certificate provider (DigiCert, Sectigo, etc.), request a reissue, and download the new certificate files.
- 16.Install the new certificate on the web server:
- 17.```bash
- 18.# Nginx
- 19.sudo cp new-cert.pem /etc/nginx/ssl/example.com.crt
- 20.sudo cp new-key.pem /etc/nginx/ssl/example.com.key
- 21.sudo systemctl reload nginx
# Apache sudo cp new-cert.pem /etc/apache2/ssl/example.com.crt sudo cp new-key.pem /etc/apache2/ssl/example.com.key sudo systemctl reload apache2 ```
- 1.Verify the new certificate is active:
- 2.```bash
- 3.openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | \
- 4.openssl x509 -noout -dates
- 5.# Should show the new notAfter date in the future
- 6.
` - 7.Set up automated renewal monitoring:
- 8.```bash
- 9.# Check expiration with a script
- 10.EXPIRY=$(openssl x509 -in /etc/nginx/ssl/example.com.crt -noout -enddate | cut -d= -f2)
- 11.EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
- 12.NOW_EPOCH=$(date +%s)
- 13.DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
- 14.echo "Certificate expires in $DAYS_LEFT days"
- 15.
`
Prevention
- Use Let's Encrypt with automated renewal (certbot renew cron) for zero-maintenance certificates
- Set up certificate expiration monitoring with alerts at 30, 14, and 7 days
- Use ACME DNS challenge for wildcard certificates that auto-renew
- Implement certificate transparency monitoring to detect unexpected changes
- Document the certificate renewal procedure for manually-purchased certificates