Web design tutorials and tools since 2014Privacy
Search 125 articles

Resources

SSL Certificate Monitoring: A Free Script, Cron Alerts and the Best Tools

An expired TLS certificate takes a site down as surely as a crashed server. Browsers show a full-page warning, and most visitors leave. Plain uptime checks often miss it until the day it expires, because the server is still answering.

This guide covers why certificates still expire on auto-renewal and how to check one by hand. It also includes a short shell script we wrote and tested that runs from cron and emails you before anything breaks, plus the hosted tools that do the same job. Tool features and prices come from each vendor’s pricing pages and help docs, checked on November 12, 2026. We did not sign up for the tools; the script, however, we ran for real, and its output is below.

The Short Answer

  • Free and self-hosted: our check-ssl.sh script plus a cron job. It checks expiry, the certificate chain and the hostname, and needs only OpenSSL.
  • Free and hosted, with SSL on the free plan: Pulsetic, or StatusCake for a single certificate. Uptime Kuma if you run your own server.
  • Not on UptimeRobot’s free plan: its help center says SSL and domain expiry monitoring are paid features, despite what the pricing table suggests.
  • Alert at 30, 14 and 7 days, and immediately on chain or hostname errors.
  • Expect more renewals. Maximum certificate lifetimes are shrinking from 200 days now to 47 days by March 2029, and Let’s Encrypt’s default drops from 90 to 64 days in February 2027.

Why Certificates Still Expire

Most certificates renew automatically now, so an expiry usually means the automation broke without anyone noticing. Common causes:

  • Failed renewal. Let’s Encrypt has to reach your server to validate the domain. A firewall rule, a changed DNS record, or a deploy that wipes the /.well-known/acme-challenge/ folder will block it. (We protect that folder in our own static site deploy for exactly this reason.)
  • The renewal ran, but the server didn’t reload. The new certificate sits on disk while the web server keeps serving the old one from memory.
  • CDN and origin mismatch. A CDN shows visitors its own certificate, so the site looks fine while the origin’s certificate quietly expires, until the CDN can no longer connect.
  • Forgotten hostnames. www, a mail server, an API subdomain or a staging site, each with its own certificate.
  • No more reminder emails. Let’s Encrypt stopped sending expiration emails on June 4, 2025. Its announcement recommends third-party monitoring instead.

Shorter lifetimes make all of this more likely. Under a schedule the CA/Browser Forum approved in April 2025, the maximum validity of a public TLS certificate fell to 200 days on March 15, 2026, falls to 100 days on March 15, 2027, and to 47 days on March 15, 2029. Let’s Encrypt, which issues 90-day certificates today, will move its default to 64 days on February 10, 2027 and to 45 days on February 16, 2028. Its own advice: once certificates last 45 days, renewing on a fixed 60-day timer won’t be enough.

Check a Certificate by Hand

In a browser, click the icon to the left of the address, then the connection or certificate details. You will see who issued it and its “valid to” date.

In a terminal, one line shows the issuer and validity dates of the certificate the server actually sends:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -issuer -dates

Run against this site on November 12, 2026, it returned a Let’s Encrypt certificate valid from September 23 to December 22, 2026: a 90-day certificate with 39 days left. Let’s Encrypt clients usually renew around 30 days before expiry, so it should be replaced in about nine days.

Two details matter. -servername sends the hostname (SNI), without which a server hosting several sites may return the wrong certificate. And openssl x509 -checkend answers “will this expire within N seconds?”, which is easier to script than parsing dates.

A DIY Monitor: Script, Cron and Email

The script below checks any number of hosts. For each, it verifies the chain and the hostname, then reports OK, WARN (under 30 days) or CRIT (under 7 days, or any certificate error). The exit code is 0, 1 or 2, so cron, CI or another monitor can act on it.

#!/bin/sh
# check-ssl.sh: warn before TLS certificates expire, and catch chain or hostname errors.
# Usage: ./check-ssl.sh example.com www.example.com shop.example.com:8443
# Exit code: 0 all OK, 1 at least one WARN, 2 at least one CRIT.

WARN_DAYS=${WARN_DAYS:-30}
CRIT_DAYS=${CRIT_DAYS:-7}
status=0

for target in "$@"; do
  host=${target%%:*}
  port=${target#*:}; [ "$port" = "$target" ] && port=443

  # Fetch the certificate the server sends, verifying the chain and the hostname.
  out=$(echo | openssl s_client -connect "$host:$port" -servername "$host" \
        -verify_hostname "$host" -verify_return_error 2>&1)
  cert=$(printf '%s\n' "$out" | openssl x509 2>/dev/null)

  if [ -z "$cert" ]; then
    reason=$(printf '%s\n' "$out" | grep -m1 -o 'verify error:[^)]*' || echo 'no certificate (connection failed)')
    echo "CRIT  $target  $reason"; status=2; continue
  fi

  end=$(printf '%s\n' "$cert" | openssl x509 -noout -enddate | cut -d= -f2)
  # GNU date, then BSD/macOS date, then BusyBox (Alpine).
  end_s=$(date -u -d "$end" +%s 2>/dev/null \
       || date -j -u -f '%b %e %T %Y %Z' "$end" +%s 2>/dev/null \
       || date -u -D '%b %d %H:%M:%S %Y' -d "${end% GMT}" +%s 2>/dev/null)
  days=$(( (end_s - $(date +%s)) / 86400 ))

  if ! printf '%s\n' "$cert" | openssl x509 -noout -checkend $((CRIT_DAYS * 86400)) >/dev/null; then
    echo "CRIT  $target  expires in $days days ($end)"; status=2
  elif ! printf '%s\n' "$cert" | openssl x509 -noout -checkend $((WARN_DAYS * 86400)) >/dev/null; then
    echo "WARN  $target  expires in $days days ($end)"; [ $status -lt 1 ] && status=1
  else
    echo "OK    $target  expires in $days days ($end)"
  fi
done
exit $status

Download check-ssl.sh (1.7 KB).

What it caught

Terminal output of check-ssl.sh: OK for webdesignerhut.com with 39 days left, CRIT for expired, wrong host, self-signed, untrusted root and incomplete chain test sites
Real output from November 12, 2026. The five failing hosts are badssl.com’s deliberately broken test certificates.

We ran it against this site and five of badssl.com’s test hosts, which exist to serve broken certificates. It flagged an expired certificate, a hostname mismatch, a self-signed certificate, an untrusted root and an incomplete chain, each with OpenSSL’s reason. Some browsers repair an incomplete chain by fetching the missing certificate, so that one can look fine in Chrome while failing in other clients. That makes it worth catching.

We tested it on macOS (OpenSSL 3.6.4), Debian 12 (OpenSSL 3.0.22) and Alpine 3.20. The first Alpine run turned up a bug. BusyBox date can’t parse OpenSSL’s date format, so the day count came out wrong, though the OK, WARN and CRIT status was still right because it comes from -checkend. The third date line in the script is the fix.

Run it daily from cron

Cron emails a job’s output to MAILTO, and filtering out the OK lines means you only get mail when something needs attention:

MAILTO=you@example.com
0 8 * * * /usr/local/bin/check-ssl.sh example.com www.example.com mail.example.com:465 | grep -v '^OK'

This needs a server that can send mail. If it can’t, or you want to know when the check itself stops running, use a dead man’s switch: a service that alerts you when a scheduled ping doesn’t arrive. Healthchecks.io monitors 20 jobs for free:

0 8 * * * /usr/local/bin/check-ssl.sh example.com www.example.com && curl -fsS -m 10 https://hc-ping.com/your-uuid

The ping is sent only when every certificate is OK. A warning, an error, or a server that stopped running cron all end the same way, with an alert from Healthchecks.

Healthchecks.io homepage with the headline Simple and Effective Cron Job Monitoring
Healthchecks.io alerts you when a scheduled job stops checking in.

The script’s blind spot is that it runs from one place. It can’t tell you whether a CDN edge in another country is serving a different certificate, and it won’t catch domain expiry. That is where hosted tools help.

Tools That Do It for You

ToolSSL expiry on freeDomain expiry on freeDefault remindersFree planPaid from
PulseticYesYes, within the 10Starts 14 days out10 monitors, heartbeats or domains, 5 min$9/mo
StatusCake1 certificate1 domain3 points you choose10 uptime monitors, 5 min€16.66/mo yearly
Uptime KumaYes (self-hosted)Yes21, 14, 7 days, editableUnlimited, on your serverFree
UptimeRobotNo, paid onlyNo, paid only30, 14, 7, 0 days50 monitors, 5 min€9/mo yearly
Better StackNot statedNot statedOff until set (1 to 60 days)10 monitors, 3 min$29/mo per responder, yearly
Oh DearNo free planNo free plann/a10-day trial$17/mo for 2 sites
Red Sift Certificates LiteYes, up to 250 certificatesNot checkedEmailFreeNot checked

“Not stated” means the pricing page and docs we read don’t say whether the feature is on the free plan; “Not checked” means we didn’t verify it. Red Sift’s free allowance comes from Let’s Encrypt’s announcement. Prices are as shown to us; UptimeRobot and StatusCake displayed euros.

Pulsetic

Pulsetic homepage with the headline Uptime monitoring and alerts. The call is yours.
Pulsetic includes SSL monitoring on its free plan.

Pulsetic’s pricing table marks SSL monitoring as included on every plan, free included, and its free allowance of 10 is shared between monitors, heartbeats and domains. Its own pages disagree on the exact reminder days: the SSL page says 14 days, 7 days and the day before, while the help center says 14, 3 and 1. Either way, the first warning comes two weeks out. Email is the only alert channel on free.

StatusCake

StatusCake homepage with the headline Monitor what matters
StatusCake’s free plan includes one SSL and one domain monitor.

StatusCake gives one SSL monitor and one domain monitor on its free plan, enough for a single site. You pick three reminder points per certificate. The Superior plan raises that to 50 of each.

Uptime Kuma

Uptime Kuma repository on GitHub, described as a fancy self-hosted monitoring tool
Uptime Kuma is free and open source, and runs on your own server.

Uptime Kuma is the self-hosted option. Certificate expiry notifications are built in, at 7, 14 and 21 days by default, and in the 2.5 releases it also tracks domain expiry through RDAP. It needs a server, ideally not the one it is watching.

UptimeRobot

UptimeRobot homepage with the headline Meet the #1 uptime monitoring service
UptimeRobot’s free plan is generous for uptime, but not for SSL.

UptimeRobot’s pricing page lists “SSL & Domain exp. monitor” in the free column, but a tooltip on the same page and its help center both say SSL monitoring is for paid plans. On free, the “Check SSL errors” option isn’t available. On paid plans, reminders go out 30, 14, 7 and 0 days before expiry, checked once a day. For auto-renewed certificates such as Let’s Encrypt, the first warning comes at 14 days instead of 30. Our UptimeRobot alternatives guide compares its free plan in detail.

Better Stack lets you set SSL and domain expiry alerts per monitor at 1, 2, 3, 7, 14, 30 or 60 days, off by default; its pricing page doesn’t say whether they are on the free plan. Oh Dear monitors certificate health and domain expiry with no free plan. Red Sift Certificates Lite is the free email service Let’s Encrypt pointed users to when it ended its own reminders, for up to 250 certificates.

What to Alert On

  • 30 days: a heads-up. For an auto-renewed certificate this is normal; check again in a week.
  • 14 days: renewal should already have happened. Look at the renewal logs.
  • 7 days or less: act now. Renew by hand if needed.
  • Any chain error: usually a missing intermediate certificate after a renewal or server change.
  • Hostname mismatch: often a new subdomain or a CDN serving the wrong certificate.
  • Issuer change: unexpected if you didn’t switch providers. Certificate Transparency monitors such as the open-source Cert Spotter alert you when any certificate is issued for your domain.

With 90-day certificates renewed at 30 days left, a 30-day warning sometimes fires right before a normal renewal. If that gets noisy, set WARN to 20 days. Once lifetimes reach 45 days, warnings at 14 and 7 days will make more sense.

Pair It With Domain Expiry Monitoring

A valid certificate is no use on a domain that has lapsed. Domain expiry is usually a billing problem: an expired card or a renewal notice sent to an old email address. Most registrars warn you, but monitoring the domain separately costs little. Pulsetic, StatusCake and Uptime Kuma include it on their free tiers; UptimeRobot and Oh Dear on paid plans. For a single domain, turning on auto-renew at your registrar and keeping the account email current covers most of the risk.

If a lapsed domain ever sends mail, check it hasn’t landed on a blocklist; our guide to email domain blacklists shows how. For broader monitoring, see our roundup of the best uptime monitoring tools, and the uptime calculator to see how much downtime a given uptime percentage allows.

FAQ

How do I check when my SSL certificate expires?

Click the icon left of the address bar and open the certificate details, or run openssl s_client with openssl x509 -noout -dates as shown above.

Does Let’s Encrypt still email before a certificate expires?

No. It stopped sending expiration emails on June 4, 2025, and recommends a third-party monitoring service instead.

Is SSL monitoring free on UptimeRobot?

No. Its help center says SSL certificate monitoring is available on paid plans, even though the pricing table lists it under free.

How many days before expiry should I be alerted?

At 30, 14 and 7 days for expiry, and immediately for chain or hostname errors. With auto-renewal, a 14-day warning is the one that really means something went wrong.

How long do SSL certificates last now?

At most 200 days since March 15, 2026, falling to 100 days in March 2027 and 47 days in March 2029. Let’s Encrypt certificates last 90 days today.

Can cron jobs be monitored too?

Yes, with heartbeat monitoring: the job pings a URL when it finishes, and the service alerts you if the ping doesn’t arrive. Healthchecks.io, Pulsetic, Better Stack and Uptime Kuma all offer it.