← Back to Blog
An expired SSL certificate is one of the most common and entirely avoidable website outages. When your certificate expires, every visitor sees "Your connection is not private" with error code NET::ERR_CERT_DATE_INVALID. The site appears completely broken — there's no way to dismiss the warning in Chrome without bypassing security settings. This guide walks through diagnosing the expiry, understanding why renewal failed, and fixing it as quickly as possible.
🔴
Your connection is not private
NET::ERR_CERT_DATE_INVALID
Attackers might be trying to steal your information from yourdomain.com (for example, passwords, messages, or credit cards). Learn more
Step 1: Confirm the Certificate Is Actually Expired
# Check expiry date via OpenSSL:
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null \
| openssl x509 -noout -dates
# notBefore=Apr 15 00:00:00 2026 GMT
# notAfter=Jul 14 23:59:59 2026 GMT ← check this date vs today
# If the site is down and you can't connect, check the certificate file directly:
openssl x509 -in /etc/letsencrypt/live/yourdomain.com/cert.pem -noout -dates
# Or check with curl (shows SSL error):
curl -sI https://yourdomain.com
# curl: (60) SSL certificate problem: certificate has expired
# Check all Let's Encrypt certificates on your system:
sudo certbot certificates
# Certificate Name: yourdomain.com
# Domains: yourdomain.com www.yourdomain.com
# Expiry Date: 2026-07-10 (EXPIRED: 5 days ago)
# Certificate Path: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
Step 2: Identify Why Auto-Renewal Failed
If you're using Certbot with auto-renewal and the certificate still expired, something broke the renewal process. Check these common failure points:
# Check Certbot renewal logs:
sudo cat /var/log/letsencrypt/letsencrypt.log | tail -100
# Common error messages and their causes:
# "Could not connect to the ACME directory" → DNS outage or firewall blocking port 80
# "Problem binding to port 80" → another process using port 80, or Apache/Nginx config issue
# "No valid A records found for yourdomain.com" → DNS changed, domain no longer points to this server
# "unauthorized: Invalid response from http://yourdomain.com/.well-known/acme-challenge/" → web root wrong
# Check if the systemd timer is active:
sudo systemctl status certbot.timer
# active (waiting) ← should show this
sudo systemctl list-timers | grep certbot
# should show: certbot.timer → certbot.service
# If the timer isn't set up, check cron:
sudo crontab -l | grep certbot
# 0 */12 * * * certbot renew --quiet ← should show a cron entry
Step 3: Renew the Certificate Now
# For Certbot (Let's Encrypt) — force renewal immediately:
sudo certbot renew --force-renewal
# Or renew a specific domain:
sudo certbot certonly --force-renewal -d yourdomain.com -d www.yourdomain.com
# After renewal, reload the web server to pick up the new certificate:
sudo systemctl reload apache2
# or:
sudo nginx -s reload
# Verify the new expiry date:
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null \
| openssl x509 -noout -enddate
# notAfter=Oct 13 00:00:00 2026 GMT ← 90 days from now
Troubleshooting: Port 80 Blocked (HTTP-01 Challenge Fails)
# Certbot HTTP-01 challenge requires port 80 to be accessible:
# Test if port 80 responds from outside your server:
curl -I http://yourdomain.com/.well-known/acme-challenge/test
# Should respond with 200 or 404 (both are fine for the test file to be created)
# If blocked by firewall (UFW):
sudo ufw allow 80/tcp
# If blocked by iptables:
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# If Apache/Nginx is not listening on port 80:
sudo netstat -tlnp | grep :80
# Alternative: use DNS-01 challenge if HTTP-01 keeps failing:
sudo certbot certonly --manual --preferred-challenges dns \
-d yourdomain.com \
-d www.yourdomain.com
Renewing a Paid Certificate (Manual Process)
# 1. Generate a new private key and CSR:
openssl req -new -newkey rsa:2048 -nodes \
-keyout yourdomain.key \
-out yourdomain.csr \
-subj "/C=US/ST=California/L=San Francisco/O=Acme Corp/CN=yourdomain.com"
# 2. Submit the CSR to your CA (DigiCert, Sectigo, etc.) via their portal
# 3. Complete domain validation (email, HTTP file, or DNS TXT record)
# 4. Download the new certificate files
# 5. Install the new certificate (Apache):
SSLCertificateFile /etc/ssl/certs/yourdomain_new.crt
SSLCertificateKeyFile /etc/ssl/private/yourdomain.key
# 6. Restart/reload:
sudo apachectl configtest && sudo systemctl reload apache2
Step 4: Set Up Expiry Monitoring to Prevent Future Outages
# Option 1: Cron job to email when certificate expires in 30 days or less:
cat > /usr/local/bin/check-ssl-expiry.sh << 'EOF'
#!/bin/bash
DOMAIN="yourdomain.com"
EXPIRY=$(echo | openssl s_client -connect $DOMAIN:443 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_TS=$(date -d "$EXPIRY" +%s)
NOW_TS=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_TS - $NOW_TS) / 86400 ))
if [ $DAYS_LEFT -lt 30 ]; then
echo "SSL certificate for $DOMAIN expires in $DAYS_LEFT days!" \
| mail -s "SSL EXPIRY WARNING: $DOMAIN" admin@yourdomain.com
fi
EOF
chmod +x /usr/local/bin/check-ssl-expiry.sh
# Run daily at 8am:
(crontab -l; echo "0 8 * * * /usr/local/bin/check-ssl-expiry.sh") | crontab -
# Option 2: Verify Certbot's auto-renewal is working:
sudo certbot renew --dry-run
# If this outputs "Congratulations, all simulated renewals succeeded"
# your auto-renewal is healthy
After fixing an expired certificate and reloading the web server, wait 2–3 minutes and test in a private browser window (not your current session, which may have cached the error). Use curl -sI https://yourdomain.com | head -5 to confirm the server responds with HTTP 200 over HTTPS.
Debugging Expired SSL Certificates on Windows
Windows Server
PowerShell
Check All Certificates in the Windows Certificate Store
# List all certificates in Personal store with expiry dates (run in PowerShell)
Get-ChildItem Cert:\LocalMachine\My | `
Select-Object Subject, NotAfter, Thumbprint | `
Sort-Object NotAfter | `
Format-Table -AutoSize
# Find certificates expiring within 30 days
$threshold = (Get-Date).AddDays(30)
Get-ChildItem Cert:\LocalMachine\My | `
Where-Object { $_.NotAfter -lt $threshold } | `
Select-Object Subject, NotAfter, Thumbprint
Check a Remote Server's Certificate Expiry
# Using PowerShell (works on Windows 10/Server 2016+)
$domain = "yourdomain.com"
$port = 443
$tcp = New-Object System.Net.Sockets.TcpClient($domain, $port)
$ssl = New-Object System.Net.Security.SslStream($tcp.GetStream())
$ssl.AuthenticateAsClient($domain)
$cert = $ssl.RemoteCertificate
$x509 = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($cert)
Write-Host "Subject: $($x509.Subject)"
Write-Host "Issuer: $($x509.Issuer)"
Write-Host "Expires: $($x509.NotAfter)"
Write-Host "Days left: $(($x509.NotAfter - (Get-Date)).Days)"
$ssl.Close(); $tcp.Close()
Check Using OpenSSL on Windows
Install OpenSSL via winget install ShiningLight.OpenSSL, then use the same commands as Linux — they work identically on Windows:
:: Check remote certificate expiry
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>nul | openssl x509 -noout -dates
:: Check a local certificate file
openssl x509 -noout -dates -in yourdomain.crt
:: Check days remaining (Windows PowerShell)
$notAfter = openssl x509 -noout -enddate -in yourdomain.crt | ForEach-Object {$_ -replace "notAfter=",""}
[int]((New-TimeSpan -End (Get-Date $notAfter)).TotalDays * -1)
Windows Event Log: When IIS fails to serve HTTPS due to an expired certificate, it logs Event ID 36882 in the Windows Event Log under System. Open Event Viewer → Windows Logs → System and filter for Source: "Schannel" to find SSL-related errors quickly.
Debugging Expired SSL Certificates on macOS
macOS
Terminal
# Check remote certificate expiry (macOS uses LibreSSL — same syntax as Linux)
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | \
openssl x509 -noout -dates
# Check days remaining until expiry
expiry=$(openssl s_client -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
echo "Certificate expires: $expiry"
echo "Days remaining: $(( ($(date -j -f "%b %d %T %Y %Z" "$expiry" +%s) - $(date +%s)) / 86400 ))"
# Check all certificates in macOS Keychain for expiry
security find-certificate -a -p /Library/Keychains/System.keychain | \
openssl x509 -noout -subject -dates 2>/dev/null | grep -A2 "yourdomain"
# If using Homebrew Apache/Nginx — check the cert file directly
openssl x509 -noout -dates -in /opt/homebrew/etc/httpd/ssl/yourdomain.crt
Monitor certificate expiry with our free SSL checker
Check your certificate's expiry date instantly and verify your auto-renewal configuration is working.