Fix NET::ERR_CERT_DATE_INVALID โ€” SSL Certificate Expired Error

intermediate๐Ÿ”’ SSL/TLS2026-03-19| Chrome/Firefox/Edge on any OS; Nginx/Apache on Ubuntu/Debian/CentOS; Let's Encrypt or commercial SSL certs

Error Message

NET::ERR_CERT_DATE_INVALID โ€” The server's security certificate has expired.
#ssl#certificate#expired#https

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 -t or sudo 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-run once 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

Related Error Notes