C
// GUIDE

How to monitor SSL certificate expiration

Expired certificates cause outages, browser warnings, and lost trust. Here is how to monitor SSL certificate expiration reliably — without building a homegrown cron job.

▸ The Gap Between Issuance and Deployment

The most common SSL monitoring mistake is watching only the certificate issuance event in Certificate Transparency logs. That tells you when a cert was issued — not whether it was actually deployed. If your Let's Encrypt auto-renewal ran but your web server still serves the old certificate because of a configuration error or a cached proxy layer, CT logs show 'all good' while your users get a browser warning. To monitor SSL certificate expiration correctly, you must probe the actual TLS handshake on port 443 and read the X.509 certificate your server presents in real time.

▸ Essential TLS Fields to Track

The fields that matter are notAfter (the hard expiry date), the issuer chain, the Subject Alternative Names (SAN) list, and the TLS version negotiated. notAfter drives your alerts. The SAN list tells you whether the cert actually covers the hostname — a wildcard cert may cover *.example.com but not example.com itself, causing a mismatch. TLS 1.2 vs 1.3 matters for compliance audits. Reading all of this requires a genuine TLS handshake, not an HTTP status check.

▸ Designing Staged, Idempotent Alerts

Set alert thresholds at 30, 14, 7 and 1 days before expiry. The 30-day mark gives you time to coordinate a renewal through a change management process. The 7-day and 1-day alerts are the safety net: if the renewal was scheduled but silent, those alerts force a manual check. Idempotent alerting matters — you want one notification per threshold crossing, not one per check cycle. Flooding on-call with hourly reminders at T-3 days defeats the purpose of staged thresholds entirely.

▸ Leverage Certificate Transparency (CT) Logs

Certificate Transparency logs add a second layer of visibility. Every publicly trusted CA must submit issued certificates to public CT logs within 24 hours of issuance. You can watch these logs for any new certificate issued for your domain names. This matters for security: if a mis-issued certificate appears for your domain — due to a compromised CA or a social-engineering attack — you will see it in CT before attackers can exploit it, giving you time to request revocation.

▸ Combining Live Probing and CT Monitoring

The combination of live TLS probing and CT monitoring closes both gaps. Live probing catches the 'renewed but not deployed' case. CT monitoring catches mis-issuance and gives you an early signal that a renewal is incoming, before the deployment reaches port 443. Running this on a schedule across all your domains — not just the main hostname but API subdomains, staging environments and internal services — turns certificate management from a reactive scramble into a predictable operation with full audit history.

▸ Multi-Channel Incident Workflows

Alerting channels should match your team's workflow. Email covers the basics and is always the default. Slack channels let you route cert alerts to the infrastructure team without polluting general channels. Webhooks integrate with PagerDuty, Opsgenie, or any custom handler — useful when cert expiry should trigger the same incident workflow as a server outage. SMS is available for highest-urgency scenarios on paid plans. The entry paid tier at $15/mo includes signed webhooks and REST API access; the free tier covers email and one Slack integration.

▸ Rolling Your Own: The Minimum Viable Monitor

If you want to start with a script, this is the honest minimum. It sweeps a list of hostnames and exits non-zero when any of them falls inside the warning window, so cron mails you the output or your CI fails:

#!/usr/bin/env bash
# ssl-check.sh — warn when any host expires within N days
DAYS=${DAYS:-30}
SECS=$((DAYS * 86400))
STATUS=0

for h in "$@"; do
  end=$(echo | openssl s_client -servername "$h" -connect "$h:443" 2>/dev/null \
        | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
  if [ -z "$end" ]; then
    printf '%-30s UNREACHABLE\n' "$h"; STATUS=1; continue
  fi
  if echo | openssl s_client -servername "$h" -connect "$h:443" 2>/dev/null \
     | openssl x509 -noout -checkend "$SECS" >/dev/null; then
    printf '%-30s OK      (%s)\n' "$h" "$end"
  else
    printf '%-30s EXPIRING (%s)\n' "$h" "$end"; STATUS=1
  fi
done
exit $STATUS

Run it as ./ssl-check.sh example.com api.example.com, or from cron with 0 7 * * * /usr/local/bin/ssl-check.sh example.com api.example.com. That is genuinely useful and takes ten minutes to write.

▸ Where the Homegrown Script Runs Out

The script above is a starting point, not a monitoring system. What it does not do is the part that costs time: it has no state, so it cannot tell you what changed; it re-alerts on every run, so it either spams you daily for a month or gets filtered into a folder nobody reads; it has no history, so when an auditor asks when a certificate changed you have nothing; it dies silently if the cron host is down, which is precisely when you need it; and it cannot see Certificate Transparency, so a mis-issued certificate for your domain goes unnoticed.

CAPABILITYCRON + OPENSSLHOSTED MONITORING
Read live expiryYesYes
Staged 30/14/7/1 alertsNeeds state you must buildBuilt in
One alert per thresholdRe-fires every runIdempotent
Change history / audit trailNoneRetained per host
CT log new-issuance alertsSeparate streaming API to implementIncluded
Fails when the checker itself is downSilentlyRedundant probes
Slack / webhook / SMS routingGlue code per channelConfigured per resource

▸ Don't Forget What Isn't a Certificate

Two adjacent expiries take sites down just as reliably and are not covered by any SSL check. The first is domain registration: if the domain lapses at the registrar, a perfectly valid certificate is worthless because DNS stops resolving. That is domain expiry monitoring, tracked over RDAP rather than TLS. The second is DNS records: an A or MX record changing without your knowledge is both an outage and a security signal, which is what DNS monitoring watches. Certificate, domain and DNS expiry are three separate clocks, and teams that only watch the first still get paged.

▸ Zero-Maintenance Certificate Lifecycle Monitoring

CertFleet handles the full loop: live TLS probe, CT log cross-reference, staged idempotent alerts, multi-channel delivery, and history. Try the free instant checker to see what a real probe returns for any domain, then add your domains for continuous monitoring — free for up to 10 SSL certificates and 10 uptime monitors, no credit card needed. See how to check a certificate by hand, why SSL monitoring matters, or how continuous monitoring works.

▸ Frequently asked questions

How often should I check SSL certificate expiration?
At least once a day, and hourly if you can. Let's Encrypt certificates last 90 days and industry maximum lifetimes are shortening, so renewals are frequent. A weekly check can leave a failed renewal undetected for six days — long enough to reach the expiry date on a short-lived certificate.
What alert thresholds should I use for certificate expiry?
30, 14, 7 and 1 day before expiry. The 30-day alert gives time to schedule a renewal through change management; the 7-day and 1-day alerts are the safety net that forces a manual check when automation has silently failed. Each threshold should fire exactly once, not on every check cycle.
Can I monitor SSL certificate expiration with a cron job?
Yes, and for a handful of hosts it is reasonable. A loop over openssl s_client piped to openssl x509 -noout -checkend gives you an exit code you can alert on. What cron does not give you is idempotent alerting, change history, monitoring of the monitor itself, or Certificate Transparency coverage — those are what you build or buy next.
Why did my certificate expire even though auto-renewal succeeded?
Because renewal and deployment are separate steps. The ACME client obtained a new certificate and wrote it to disk, but the web server was never reloaded, so it continued serving the old certificate from memory until it expired. Certificate Transparency and your CA dashboard both show the renewal as successful. Only a live TLS handshake on port 443 reveals the mismatch.
What is Certificate Transparency monitoring and do I need it?
Publicly trusted CAs must log every certificate they issue to public Certificate Transparency logs. Watching those logs for your domain names tells you when a certificate is issued — including one you did not request, which is an early signal of CA compromise or a social-engineering attack. It complements live probing rather than replacing it: CT sees issuance, probing sees deployment.
Should I monitor internal and staging certificates too?
Yes. Internal services, staging environments and API subdomains rarely share a renewal schedule with the main site, and they are the ones nobody remembers. An expired internal certificate typically surfaces as a confusing service-to-service failure rather than an obvious browser warning, which makes it slower to diagnose.
▸ START MONITORING

CertFleet probes the live certificate, watches Certificate Transparency, and alerts you 30/14/7/1 days before expiry. Free for 10 certificates, no card.

Built in France by a developer for real operational needs. Read our architecture, team story and full RGPD details →