How to check SSL certificate expiry and monitor it automatically (before certificates shrink to 47 days)
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.
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 issued | Maximum validity | Renewals per year (at least) |
|---|---|---|
| Before March 15, 2026 | 398 days | ~1 |
| From March 15, 2026 | 200 days | ~2 |
| From March 15, 2027 | 100 days | ~4 |
| From March 15, 2029 | 47 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
- Hostname match: the certificate must cover the exact name (including
www.). Look at the Subject Alternative Names:openssl x509 -noout -ext subjectAltName. - Complete chain: a missing intermediate certificate works in some browsers and fails in others and in API clients.
openssl s_client -showcertsshows what the server sends. - HTTP to HTTPS redirect:
curl -sI http://example.com | grep -i locationshould point athttps://. - Security headers:
curl -sI https://example.comand look forStrict-Transport-Security,Content-Security-Policy,X-Content-Type-Options,Referrer-PolicyandPermissions-Policy. They often disappear after a CDN or hosting change. - Every hostname you own: APIs, staging, mail and status subdomains expire too. Certificate Transparency search (for example crt.sh) lists certificates issued for your domain, which is a good way to find forgotten hosts.
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 HubDisclosure: 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
- How to check SPF, DKIM and DMARC records (and fix “too many DNS lookups”)
- How to find broken links on your website, free, and get a weekly report
Sources: CA/Browser Forum Baseline Requirements section 6.3.2 and Ballot SC-081v3; OpenSSL documentation for s_client and x509.