EdenWorks

How to check SSL certificate expiry and monitor it automatically (before certificates shrink to 47 days)

By EdenWorks · Published 2026-11-02 · Updated 2026-11-02

Short version. Run openssl s_client against port 443 and read notAfter, or use openssl x509 -checkend to get a yes/no answer for “expires within N days”. Because public certificates issued since March 15, 2026 can last at most 200 days (100 days from March 2027, 47 days from March 2029), renewals now happen two to eight times more often, so a scheduled check is no longer optional.

On this page
  1. Why expiry monitoring matters more every year
  2. Check expiry with openssl (Linux/macOS)
  3. Check with curl, PowerShell or a browser
  4. A 20-line script with a 30-day warning
  5. What else to check besides the date
  6. Monitor many sites automatically

Why expiry monitoring matters more every year

In April 2025 the CA/Browser Forum passed Ballot SC-081v3, which shortens the maximum life of publicly trusted TLS certificates in three steps:

Certificate issuedMaximum validityRenewals per year (at least)
Before March 15, 2026398 days~1
From March 15, 2026200 days~2
From March 15, 2027100 days~4
From March 15, 202947 days~8

Many CAs issue one day short of the maximum (for example 199 days). Certificates keep the lifetime they were issued with, so you will see a mix for a while.

More renewals means more chances for automation to fail silently: a DNS change breaks the ACME challenge, a load balancer keeps serving the old certificate, or a forgotten subdomain was renewed by hand. Let's Encrypt also stopped sending expiry reminder emails in 2025, so many people lost the safety net they did not know they relied on.

1. Check expiry with openssl (Linux and macOS)

Ask the server for its certificate and print the dates. Always pass -servername so you get the right certificate on shared hosting and CDNs (SNI):

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -enddate -subject -issuer
# notAfter=Jan 30 23:59:59 2027 GMT
# subject=CN=example.com
# issuer=C=US, O=Let's Encrypt, CN=R11

For a yes/no answer in scripts, use -checkend with a number of seconds. This exits with status 1 if the certificate expires within 30 days (2,592,000 seconds):

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -checkend 2592000 \
  && echo "OK: more than 30 days left" || echo "WARNING: expires within 30 days"

To check a certificate file on disk instead of a live server:

openssl x509 -in /etc/ssl/certs/example.pem -noout -enddate

2. Check with curl, PowerShell or a browser

curl

curl -vsI https://example.com 2>&1 | grep -iE "expire date|start date|issuer"

The exact labels depend on the TLS library your curl was built with; on most Linux and macOS builds you will see expire date:.

Windows PowerShell

$req = [Net.HttpWebRequest]::Create("https://example.com")
$req.GetResponse().Dispose()
$req.ServicePoint.Certificate.GetExpirationDateString()

This works in Windows PowerShell 5.1. In PowerShell 7 the simplest route is to call openssl (it ships with Git for Windows) using the commands above.

Browser

Click the padlock or site-information icon next to the address bar, open the certificate details, and read “Valid to” / “Expires on”. Fine for one site, not for twenty.

3. A 20-line script with a 30-day warning

Put your hostnames in sites.txt, one per line, and run this daily from cron. It prints one line per site and exits non-zero if anything needs attention, which most schedulers can turn into an alert.

#!/usr/bin/env bash
# check-certs.sh  (needs bash, openssl and GNU date)
WARN_DAYS=30
status=0
while read -r host; do
  [ -z "$host" ] && continue
  end=$(echo | timeout 10 openssl s_client -connect "$host:443" -servername "$host" 2>/dev/null \
        | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
  if [ -z "$end" ]; then
    echo "ERROR    $host  could not read certificate"; status=1; continue
  fi
  days=$(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 ))
  if [ "$days" -lt "$WARN_DAYS" ]; then
    echo "WARNING  $host  $days days left ($end)"; status=1
  else
    echo "OK       $host  $days days left"
  fi
done < sites.txt
exit $status
# crontab -e  (every day at 08:00)
0 8 * * * /usr/local/bin/check-certs.sh || /usr/local/bin/notify-me.sh

On macOS, replace date -d "$end" with date -j -f "%b %e %T %Y %Z" "$end". With shorter certificate lifetimes, a fixed 30-day threshold can become noisy (a 47-day certificate is “within 30 days” for most of its life). A better rule for the future is to warn when less than about a third of the certificate's lifetime remains.

4. What else to check besides the date

5. Monitor many sites automatically

If you look after a handful of sites, the script above plus cron is enough. Once you manage client sites, you usually want: daily checks, a memory of what was already reported (so you are not alerted every day about the same thing), separate routes for critical and warning issues, and a weekly summary.

If you use n8n, our free template does that. It checks the certificate, HTTPS reachability, the HTTP→HTTPS redirect and a security-headers grade for every site in one list, stores results in an n8n Data Table, and alerts Slack or Gmail only when something is new, gets worse or is resolved.

Free: SSL Expiry & Security Headers Monitor (n8n template)

Approved and published on the n8n Creator Hub. Pay what you want, $0 is fine.

Get the free template View it on the n8n Creator Hub

Disclosure: the template runs checks through an Apify Actor that EdenWorks built and publishes. You pay Apify's usage price (about $0.003 per site per day; Apify's free plan credits cover light use), and EdenWorks earns part of that fee. Everything on this page can be done by hand for free.

Related guides

Sources: CA/Browser Forum Baseline Requirements section 6.3.2 and Ballot SC-081v3; OpenSSL documentation for s_client and x509.