SSL Certificate Expiry Checker: How to Monitor and Avoid Outages
Every year, multi-billion-dollar companies — Microsoft Teams, LinkedIn, GitHub Pages, Cisco — go down because someone forgot to renew an SSL certificate. The fix takes minutes. The outage costs millions. And the worst part? An SSL certificate expiry checker running once a day would have prevented all of it.
If you're managing one site, Let's Encrypt's auto-renewal probably has you covered. If you're managing dozens of subdomains, microservices, internal tools, and legacy apps with manually-issued certs, you have a ticking time bomb. This guide shows you how to check SSL expiration manually, set up automated monitoring, and integrate cert checks into a broader security posture.
Check your site right now: Free ZeriFlow scan in 60 seconds →
Why SSL Certificates Expire (and Why That's Now a Bigger Problem)
SSL/TLS certificates have always had expiration dates — historically 1-3 years. But the trend is aggressively shorter:
- 2020: Apple enforced 1-year max validity
- 2023: Industry shift to 90-day certs (driven by Let's Encrypt)
- 2026: Apple, Google, and Mozilla pushing for 47-day max validity by 2029
Shorter validity is a security win — compromised certs have less time to be abused. But operationally, it means you can't rely on a calendar reminder once a year. You need automation.
The most common reasons certs expire unexpectedly:
- 1Manual cert on a server outside your IaC. Renewal happens once, then the engineer leaves.
- 2Auto-renew breaks silently. Cron job dies, ACME challenge fails, DNS provider changes API.
- 3Forgotten subdomains.
staging.example.comwas a one-off, never added to monitoring. - 4Internal services with self-signed or private CA certs. Often have shorter lifetimes and no renewal automation.
- 5Cert pinning in mobile apps. Pinned cert expires, app stops working until force-update.
Method 1: Check SSL Expiry Manually with openssl
The fastest one-liner for a single domain:
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
| openssl x509 -noout -datesOutput:
notBefore=Jan 15 00:00:00 2026 GMT
notAfter=Apr 15 23:59:59 2026 GMTFor a one-shot expiration date only:
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
| openssl x509 -noout -enddateDays until expiry:
expiry=$(echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
echo "$(( ($(date -j -f "%b %d %T %Y %Z" "$expiry" +%s) - $(date +%s)) / 86400 )) days"Method 2: Check SSL Expiry with curl
Curl is great for checking the full chain in one shot:
curl -vI https://example.com 2>&1 | grep -E "expire|subject|issuer"Or just the expiration:
curl --insecure -vvI https://example.com 2>&1 | awk '/expire date:/{print}'Method 3: Online SSL Expiry Checkers
For a quick visual check, online tools work well:
- SSL Labs (ssllabs.com/ssltest) — deep analysis, grading, chain validation
- whynopadlock.com — shows mixed-content issues alongside cert info
- ZeriFlow — checks SSL expiry as part of an 80+ point audit, including cipher strength, HSTS, certificate transparency, and chain validity
The advantage of an integrated scanner like ZeriFlow is that you don't just learn that your cert expires in 23 days — you learn whether your TLS config has weak ciphers, your HSTS preload is correctly set, and your certificate chain is complete (a common silent failure).
Method 4: Programmatic Monitoring (the Right Way)
Manual checks are fine for ad-hoc debugging. For production, you need automation. Three patterns:
A) Bash + cron + alerts
A simple script that checks N domains and pings Slack at 14 days:
#!/bin/bash
DOMAINS=("example.com" "api.example.com" "admin.example.com")
THRESHOLD_DAYS=14
for domain in "${DOMAINS[@]}"; do
expiry=$(echo | openssl s_client -servername "$domain" -connect "$domain:443" 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$expiry" +%s)
now_epoch=$(date +%s)
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
if [ "$days_left" -lt "$THRESHOLD_DAYS" ]; then
curl -X POST "$SLACK_WEBHOOK" -d "{\"text\":\"WARNING: $domain SSL expires in $days_left days\"}"
fi
doneRun via cron daily:
0 9 * * * /opt/scripts/ssl-check.shB) Prometheus blackbox_exporter
If you already run Prometheus, the blackbox_exporter exposes probe_ssl_earliest_cert_expiry:
- alert: SSLCertExpiringSoon
expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 14
for: 1h
labels:
severity: warning
annotations:
summary: "SSL cert for {{ $labels.instance }} expires in <14 days"C) Hosted monitoring (ZeriFlow, UptimeRobot, Better Uptime)
Hosted services remove the maintenance overhead entirely. ZeriFlow checks SSL expiry alongside 80+ other security signals — TLS version, weak ciphers, HSTS configuration, mixed content, and certificate chain validity — every time you scan.
For broader coverage of TLS configuration beyond just expiry, see our web application security testing guide.
Common SSL Expiry Mistakes (and Fixes)
Mistake 1: Monitoring only the apex domain
example.com is renewed, but cdn.example.com and webhook.example.com use separate certs that aren't tracked. Fix: maintain a central inventory of every TLS endpoint and monitor each one.
Mistake 2: Monitoring only port 443
APIs on port 8443, mail servers on 465/993, internal services on custom ports — all need monitoring too. Adapt the openssl command's :443 to the correct port.
Mistake 3: Trusting auto-renewal blindly
Let's Encrypt's certbot is excellent, but the renewal cron can fail silently if: - HTTP-01 challenge port 80 gets blocked by a firewall change - DNS-01 API credentials expire - The renewal script outputs to a deleted log file
Always monitor the cert expiry itself, not just the renewal job.
Mistake 4: Ignoring intermediate certificate expiry
Your leaf cert is valid, but the intermediate CA in your chain expired (this happened to many sites when Let's Encrypt's DST Root CA X3 expired in 2021). Fix: validate the full chain, not just the leaf. Tools like SSL Labs and ZeriFlow do this by default.
Mistake 5: Setting alerts at 7 days
If the renewal pipeline is broken, 7 days isn't enough buffer to investigate, fix, and redeploy across multiple environments. Fix: alert at 30 days (warning), 14 days (critical), 7 days (page on-call).
What to Do When a Cert Has Already Expired
If you're reading this because production is down right now:
- 1Issue a new cert immediately. Let's Encrypt:
certbot renew --force-renewal. Commercial CA: log in, reissue. - 2Deploy the new cert to all servers/load balancers. Don't forget CDN edges and mobile pinning.
- 3Verify with
openssl s_clientfrom outside your network. - 4Communicate. Status page update, customer email if it lasted more than a few minutes.
- 5Post-mortem. Why didn't monitoring catch it? Add the missing alert before the next outage.
FAQ
### Q: How often should I check SSL certificate expiry? A: Daily, automated. With 90-day certs becoming standard, weekly checks risk missing the renewal window. Set up automated monitoring with alerts at 30, 14, and 7 days before expiry.
### Q: What's the simplest command to check SSL expiry?
A: echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate — works on macOS and Linux, no install needed beyond openssl.
### Q: Why did my Let's Encrypt cert expire if I have certbot? A: Most commonly: a firewall change blocked the HTTP-01 challenge port, the DNS provider's API credentials rotated, or a server migration left the renewal cron behind. Always monitor cert expiry itself, not the renewal job.
### Q: Do wildcard certificates expire differently? A: No — same expiry rules apply. The risk with wildcards is broader: a single missed renewal takes down every subdomain. Treat wildcard renewals as P0 alerts.
### Q: Does ZeriFlow monitor SSL certificate expiry? A: Yes — every ZeriFlow scan reports certificate expiration date, days remaining, full chain validity, weak ciphers, HSTS configuration, and certificate transparency. Run scans manually any time, or upgrade for scheduled monitoring with alerts.
Conclusion
SSL expiry outages are 100% preventable. The tools are free, the scripts take 30 minutes to write, and hosted services automate everything for the price of a coffee. The only reason production goes down for an expired cert is that nobody set up monitoring — or the monitoring covered the apex domain and missed legacy-api.example.com.
Don't be the next outage post-mortem. Audit every TLS endpoint your team owns, set up daily expiry checks, and alert at 30 days minimum. Then layer in TLS config quality (cipher strength, HSTS, chain validity) so a renewed cert is actually a *secure* cert.
Start your free security scan on ZeriFlow → — 60 seconds, full SSL/TLS analysis plus 80+ other checks, free plan available.