SSL certificate expiry: check the date and catch problems early
Read an SSL certificate's expiry date with one openssl command, script the check, and fix the setups where auto-renewal quietly fails.
LLaunchScaler·Published ·8 min read
To check an SSL certificate's expiry date, run echo | openssl s_client -connect yoursite.com:443 -servername yoursite.com 2>/dev/null | openssl x509 -noout -enddate, which prints the notAfter date the certificate stops being valid. Auto-renewal is what usually keeps that date moving, and it fails quietly when DNS moves, a CAA record stops listing your certificate authority, or the validation request is blocked, so check the date and the chain on a schedule rather than trusting the renewal job.
The check takes a second per hostname. The cost of skipping it is a full-page browser warning in place of your site.
How do you check an SSL certificate's expiry date?
Connect to the host with openssl s_client, pass the certificate it returns to openssl x509, and print the dates. Use the hostname visitors use, with -servername set to it, because a server that hosts several sites picks the certificate from that name. Run it once for every hostname you serve, since each can have its own certificate.
-connect yoursite.com:443 opens a TLS connection to that host and port.
-servername yoursite.com sets the SNI name. OpenSSL fills it from -connect when that is a DNS name, but setting it explicitly avoids testing the wrong site when you connect by IP address.
echo | closes the connection once the handshake completes; without it, waits for input.
Questions, answered
What people ask about this
01
How do I check when an SSL certificate expires?
Run echo | openssl s_client -connect yoursite.com:443 -servername yoursite.com 2>/dev/null | openssl x509 -noout -enddate. It prints a notAfter line with the expiry date and time in GMT.
openssl x509 -noout -enddate "prints out the expiry date of the certificate, that is the notAfter date," and -noout suppresses everything else.
Swap -enddate for -dates to see the start date too, or add -subject -issuer to confirm which certificate and which authority you are looking at. -ext subjectAltName lists every hostname the certificate covers, which catches the case where www and the apex are served different certificates.
How do you check certificate expiry in a script or cron job?
Use openssl x509 -checkend, which takes a number of seconds and "exits nonzero if yes it will expire or zero if not." That exit code is all a cron job or CI step needs. Pick a threshold that leaves time to fix a failed renewal, and loop over every hostname you serve.
#!/usr/bin/env bash
# cert-check.sh: alert when any certificate expires within 14 days (1,209,600 seconds)
for host in yoursite.com www.yoursite.com app.yoursite.com api.yoursite.com; do
if ! echo | openssl s_client -connect "$host:443" -servername "$host" 2>/dev/null \
| openssl x509 -noout -checkend 1209600 >/dev/null; then
echo "Certificate for $host expires within 14 days or could not be read" \
| mail -s "Certificate warning: $host" you@yoursite.com
fi
done
Choose the threshold from how your certificates renew. Let's Encrypt's default certificates "are valid for 90 days," and it recommends "renewing 90 day certificates every 60 days." A 90-day certificate with fewer than 30 days left has therefore already missed its planned renewal, so a warning at 30 days and an urgent alert at 14 days gives two chances to fix it.
Do not rely on the certificate authority to warn you. Let's Encrypt ended its expiration notification emails on June 4, 2025, and recommends a third-party monitoring service instead.
Which hostnames and services should you check?
Check every name that serves TLS to a visitor, an API client or another server, not only the marketing site. Each one can carry its own certificate with its own renewal path, and the forgotten ones are the ones that expire. Build the list from your DNS zone rather than from memory.
The apex and www, which often sit on different certificates or even different hosts.
The app, API and auth subdomains your product and your customers' integrations call.
Docs, status and help-centre subdomains run by a third party, whose renewal you do not control but whose expiry still shows your name.
Mail servers. openssl s_client can switch a plain connection to TLS before reading the certificate with -starttls smtp, for example -connect mail.yoursite.com:587 -starttls smtp.
Any old hostname still redirecting to your site. A visitor following an old link hits its certificate before the redirect.
For each name, confirm it appears in the certificate's subjectAltName list. A certificate that is in date but does not cover the hostname fails just as an expired one does.
Why does auto-renewal fail?
Auto-renewal fails when the certificate authority can no longer confirm you control the domain, or when the renewal job stops running. Both happen without an error anyone sees: the old certificate keeps working until its last day. Most failures trace back to a change made for another reason, such as a DNS move, a new firewall rule or a new CDN.
Cause
What breaks
How to check
Fix
DNS moved to a new host or CDN
The HTTP-01 challenge reaches a server where your renewal client does not run
Compare the A/AAAA records with where the client is installed
Renew on the new host, or let the new host or CDN manage the certificate
A CAA record excludes your authority
The authority refuses to issue
dig CAA yoursite.com
Add your authority, for example yoursite.com. CAA 0 issue "letsencrypt.org"
Open port 80 and exempt that path from firewall, auth and maintenance rules
A redirect to a non-standard port
Validation rejects the redirect
Follow the redirect chain from port 80
Redirect only to ports 80 or 443
DNS-01 credentials expired
Wildcard renewals fail
Read the renewal client's log
Rotate the DNS API credentials
The renewal timer or cron job stopped
Nothing attempts renewal
Check the job's last run time
Restore the job and alert on its failure
The details come from Let's Encrypt's own documentation. HTTP-01 puts a file at http://<YOUR_DOMAIN>/.well-known/acme-challenge/<TOKEN> and "can only be done on port 80"; it follows redirects "up to 10 redirects deep" but "only to ports 80 or 443." For CAA, the authority "will always respect the CAA record closest to the domain name it is issuing a certificate for," checking www.community.example.org, then community.example.org, then example.org, and CAA checks follow CNAMEs, so a record on a CNAME target you do not control can block you.
Renewal also happens more often each year. Let's Encrypt moved its tlsserver profile to 45-day certificates on May 13, 2026, plans to issue 64-day certificates on its default profile from February 10, 2027, and 45-day certificates from February 16, 2028. Shorter lifetimes mean a broken renewal reaches expiry sooner, which makes an independent expiry check more useful, not less.
What is a certificate chain problem?
A chain problem means the server sends its own certificate without the intermediate certificate that links it to a trusted root, or sends the wrong intermediate. The certificate is valid and in date, yet clients that cannot build the chain reject the connection. It often appears after a manual install or a certificate change on a load balancer.
Check what the server actually sends with -showcerts:
OpenSSL notes that -showcerts displays the certificate list "as sent by the server" and that "it is not a verified chain." Read the Verify return code line: 0 (ok) is a working chain. unable to verify the first certificate means the chain holds a single certificate that is not self-signed, the usual sign of a missing intermediate. unable to get local issuer certificate means an issuer in the chain could not be found. certificate has expired means "the notAfter date is before the current time."
Test the chain with openssl or curl rather than your own browser. A browser that has met the intermediate before may complete the chain itself and hide a problem that API clients, webhooks and other services still hit.
What happens when an SSL certificate expires?
Once the notAfter time passes, browsers replace your site with a full-page certificate warning, and clients that verify certificates refuse to connect. Visitors, payment and webhook callbacks, API clients and crawlers that validate TLS all stop at the same point. The server is still up, so a basic uptime check that ignores certificate errors keeps reporting it as healthy.
HSTS makes it harder still. RFC 6797 says browsers should fail secure connection errors on an HSTS host "with no user recourse," so visitors cannot click through the warning. HSTS is still the right setting, but it raises the cost of an expired certificate; the trade-offs are in the HSTS header guide.
An expired certificate on the http:// to https:// redirect path breaks the redirect too, since the browser fails on the HTTPS hop. Setting up the HTTP to HTTPS redirect covers the redirect itself, and uptime vs SEO monitoring explains which monitors notice certificate failures and which report the site as up.
What should you monitor, and when should it alert?
Monitor the expiry date, the chain and the renewal path for every hostname, and alert early enough that a failed renewal is an inconvenience rather than an outage. The expiry date is the lagging signal; a CAA change, a closed port 80 or a DNS move is the leading one.
Check
How often
Alert when
Days until notAfter, per hostname
Daily
Fewer than 30 days (warning), fewer than 14 (urgent)
Chain verification
Daily, and after any certificate change
Verify return code is anything but 0
CAA records
Weekly, and after DNS changes
The record changes or stops listing your authority
/.well-known/acme-challenge/ reachable on port 80
Weekly, and after firewall or CDN changes
It returns a redirect to another port, a 403 or a login page
The renewal job's last successful run
Daily
Older than your renewal interval
Catch certificate and TLS problems with the rest of the site
A daily expiry script covers the date. LaunchScaler Watch covers the wider security picture for one website at $29/mo: its re-scan every two weeks checks for a valid, complete, unexpired certificate chain alongside CAA records, the HTTP to HTTPS redirect, HSTS and the TLS versions your server accepts, emails a diff on every run with the evidence behind each change, runs uptime checks on your health endpoint in between, and alerts you when a check drops below the bar. Start watching.
Pipe the certificate into openssl x509 -noout -checkend followed by a number of seconds. OpenSSL exits nonzero if the certificate expires within that many seconds, so -checkend 1209600 fails when fewer than 14 days are left.
03
Why did my auto-renewing certificate expire?
Usually because validation failed: DNS now points somewhere the renewal client does not run, a CAA record no longer lists your certificate authority, or port 80 or the /.well-known/acme-challenge/ path is blocked. The renewal job can also simply have stopped running.
04
Does Let's Encrypt still email before a certificate expires?
No. Let's Encrypt ended its expiration notification emails on June 4, 2025 and recommends a third-party monitoring service instead.
05
What happens to my site when the SSL certificate expires?
Browsers show a full-page certificate warning instead of your site, and clients that verify certificates, including curl by default, refuse the connection. If the site sends HSTS, visitors cannot click through the warning at all.