SSL certificate expiry is one of the most preventable causes of outages. The date is embedded in the certificate itself. You can check it months in advance. There's no surprise — just a failure to look.
And yet it keeps happening, because most setups have a monitoring gap somewhere: the renewal cron job that silently failed, the CDN cert that's separate from the origin cert, the domain someone forgot was still pointing at a server.
Here's how to actually monitor it.
The 200-day rule
Most guides say to alert at 30 days. That's too late. Here's why:
Let's Encrypt certificates expire every 90 days. They're designed to auto-renew at 60 days remaining. If auto-renewal breaks the day after issuance, you have 89 days of silent failure before the 30-day alert fires.
Check at 200 days. If something is expiring in under 200 days and it shouldn't be — your renewal pipeline is broken and you have time to fix it before anything actually breaks.
The 30-day alert is for catching emergencies. The 200-day check is for catching broken renewal pipelines while you still have runway.
The CDN trap
If your site is behind Cloudflare, Fastly, or any other CDN, you have two certificates:
- The CDN's certificate — the one your visitors see
- Your origin certificate — the one between the CDN and your server
Most monitoring tools check from the outside. They see the CDN cert — which is usually auto-managed and fine. The origin cert can be expiring unnoticed behind the CDN's edge.
Check your origin cert directly:
openssl s_client -connect YOUR_SERVER_IP:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates
And check what the CDN is serving to visitors:
openssl s_client -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates
Both should have comfortable expiry dates. The origin cert is the one that breaks silently.
A simple monitoring script
This checks a list of domains and emails you if anything expires within 200 days:
#!/bin/bash
# /usr/local/bin/check-ssl.sh
DOMAINS=("yourdomain.com" "api.yourdomain.com" "app.yourdomain.com")
ALERT_DAYS=200
EMAIL="you@yourdomain.com"
for domain in "${DOMAINS[@]}"; do
expiry=$(openssl s_client -connect "$domain:443" -servername "$domain" 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "$expiry" ]; then
echo "WARN: $domain — could not fetch cert" | mail -s "SSL Check: $domain" "$EMAIL"
continue
fi
expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null || date -jf "%b %e %T %Y %Z" "$expiry" +%s)
now_epoch=$(date +%s)
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
if [ "$days_left" -lt "$ALERT_DAYS" ]; then
echo "$domain expires in $days_left days ($expiry)" | mail -s "SSL expiry warning: $domain ($days_left days)" "$EMAIL"
fi
done
Add to cron to run weekly:
0 9 * * 1 flock -n /tmp/ssl-check.lock /usr/local/bin/check-ssl.sh
Checking multiple domains at once
If you manage more than a few domains, the command-line approach gets unwieldy. The ConfigClarity SSL Checker takes a list of domains, fetches their certificates via crt.sh, and flags anything expiring within 200 days — along with a note for CDN-fronted domains where the cert ownership is ambiguous.
The renewal pipeline audit
If you're using certbot, verify the renewal is actually working:
# Test renewal without actually renewing: certbot renew --dry-run # Check the certbot timer is active (systemd): systemctl status certbot.timer # Check the renewal log: cat /var/log/letsencrypt/letsencrypt.log | grep -E "Cert not|renewed|error"
A common failure mode: the certbot renewal works fine for a year, then a server migration or DNS change breaks the ACME challenge. The renewal silently fails. You find out 89 days later when the 30-day alert fires. By then you're under pressure.
Run certbot renew --dry-run monthly. Add it to cron. It takes 10 seconds and tells you immediately if something is broken.
The ConfigClarity SSL Checker flags certificates expiring within 200 days — not the standard 30. Paste a list of domains and get expiry status, CDN detection, and chain validation in one pass.
Check multiple domains at once — expiry dates, CDN detection, certificate chain, and 200-day early warnings.
Open SSL Checker →