The Error
You open your browser and instead of your website, you get this:
NET::ERR_CERT_DATE_INVALID
The server's security certificate has expired.
Firefox phrases it differently:
SEC_ERROR_EXPIRED_CERTIFICATE
Your connection is not secure.
Most users won't click through the scary warning โ they'll just leave. Your site is effectively down until this is fixed.
Root Cause
Every SSL/TLS cert has a hard expiry date. Let's Encrypt certs die after 90 days. Commercial certs from DigiCert or Sectigo last 1โ2 years. Once that date passes, browsers refuse the connection โ no exceptions, no grace period.
Why did it expire without you noticing?
- Auto-renewal cron job failed silently
- Certbot renewed the cert but didn't reload Nginx/Apache โ so the old cert stayed in memory
- Port 80 or DNS was broken during the renewal attempt
- The cert was installed manually with no renewal automation set up
- Expiry warning emails went to spam or a dead inbox
Quick Diagnosis
Before touching anything, confirm the cert is actually expired and not just misconfigured.
Check the expiry date from the command line:
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates
Example output:
notBefore=Jan 1 00:00:00 2024 GMT
notAfter=Apr 1 00:00:00 2024 GMT
If notAfter is in the past, the cert is expired. Next, find which cert file your server is actually loading:
# Nginx
grep -r 'ssl_certificate ' /etc/nginx/
# Apache
grep -r 'SSLCertificateFile' /etc/apache2/
Fix: Let's Encrypt (Certbot)
Let's Encrypt is the most common setup. Start by forcing a renewal:
sudo certbot renew --force-renewal
If it succeeds, reload your web server:
# Nginx
sudo systemctl reload nginx
# Apache
sudo systemctl reload apache2
If renewal fails, run with --dry-run to see the error without making changes:
sudo certbot renew --force-renewal --dry-run
Three common failure causes:
- Port 80 blocked: Certbot's HTTP-01 challenge requires port 80. Open it:
sudo ufw allow 80 - DNS not pointing to this server: The domain must resolve to this server's IP. Verify with
dig yourdomain.com - Web server config error: A broken config prevents Certbot from serving the challenge file. Test with
sudo nginx -torsudo apachectl configtest
Fix: Commercial or Manually Installed Certificate
Bought your cert from DigiCert, Sectigo, or another CA? Renew through their dashboard and install the new files manually.
First, generate a new CSR:
openssl req -new -newkey rsa:2048 -nodes \
-keyout yourdomain.key \
-out yourdomain.csr \
-subj "/C=US/ST=State/L=City/O=YourOrg/CN=yourdomain.com"
Submit the CSR to your CA, download the new cert bundle, then update your server config:
# Nginx โ update these paths
ssl_certificate /etc/ssl/yourdomain/yourdomain.crt;
ssl_certificate_key /etc/ssl/yourdomain/yourdomain.key;
# Apache โ update these paths
SSLCertificateFile /etc/ssl/yourdomain/yourdomain.crt
SSLCertificateKeyFile /etc/ssl/yourdomain/yourdomain.key
SSLCertificateChainFile /etc/ssl/yourdomain/ca-bundle.crt
Reload the web server after updating, then verify the new expiry date.
Fix: Auto-Renewal Wasn't Running
Nine times out of ten, this is the real culprit โ the renewal job ran but something broke, or it never ran at all. Check whether the systemd timer or cron job is active:
# Check systemd timer (most modern systems)
systemctl status certbot.timer
# Check cron
crontab -l
cat /etc/cron.d/certbot
If certbot.timer is inactive, enable it:
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
No cron entry? Add one:
sudo crontab -e
# Add this line:
0 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx"
The --post-hook flag is critical. Without it, Certbot writes the new cert to disk โ but Nginx keeps serving the old one from memory. The site stays broken even though renewal technically "succeeded".
Verify the Fix
Run the expiry check again:
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates
notAfter should now be ~90 days out (Let's Encrypt) or 1โ2 years (commercial cert).
Check the full certificate chain too:
curl -vI https://yourdomain.com 2>&1 | grep -E 'SSL|expire|issuer'
Or run a full chain test at https://www.ssllabs.com/ssltest/ โ it catches intermediate cert issues that openssl alone might miss.
Clear your browser cache (Ctrl+Shift+Delete) and reload. The NET::ERR_CERT_DATE_INVALID warning should be gone.
Prevent It From Happening Again
- Monitor cert expiry: UptimeRobot's free tier can alert you 30 days before expiry โ takes about 2 minutes to configure
- Test auto-renewal monthly: Run
sudo certbot renew --dry-runonce a month to confirm the full renewal path works - Always include --post-hook: The renewal command must reload the web server on success, or the new cert never takes effect
- Review renewal logs:
cat /var/log/letsencrypt/letsencrypt.logโ especially after OS updates that might change firewall rules or DNS settings - Watch the Certbot email: Certbot sends expiry warnings to the address registered with your cert. Make sure that inbox is real and someone actually reads it

