What an expired TLS certificate breaks, and how to get warned early
An expired certificate is a full outage, not a degradation. Where renewal fails, which clients break first, and how to set a warning that leaves time to fix it.
An expired TLS certificate does not slow a service down. Browsers show a full-page warning, API clients refuse to connect, and your own health checks fail, all at the same second. The fix is usually small, but it has to happen after the outage starts unless something warned you earlier.
What actually breaks
A certificate carries a "not after" timestamp. Once the clock passes it, a client doing normal validation must reject the connection. There is no grace period and no partial failure.
- Browsers block the page behind an interstitial. Most visitors leave; a few click through, and you should not want that habit in your users either.
- API clients and SDKs fail the request with a verification error. Well-written ones do not retry their way around it, because that would defeat the point of TLS.
- Webhooks you send and receive stop. The receiving side drops your deliveries, and your calls to a partner with a lapsed certificate fail in your code, not theirs.
- Internal service-to-service calls break wherever mutual TLS or a private CA is in use, often far from anything you think of as "the website".
- Mobile apps that pin a certificate or its chain can fail until a new app version ships, which no server-side fix can speed up.
The common thread is that the failure is total and immediate. Nothing degrades gracefully, so there is no early symptom to notice.
Why it still happens when renewal is automated
Automatic issuance has made expiry rarer, and it has also changed who gets hurt. Certificates from ACME-based authorities are short-lived (Let's Encrypt issues 90-day certificates), and the industry is moving to shorter maximum lifetimes still. Shorter lifetimes mean more renewals, so a renewal path that works 99% of the time fails more often in absolute terms. These are the failures we see described again and again:
- The renewal job stops running. A cron entry lost in a server rebuild, a timer that was never enabled, a container that no longer ships the client. The job cannot report its own absence.
- Renewal runs but cannot validate. A DNS record was moved, a firewall rule now blocks the HTTP-01 challenge, an API token for the DNS provider expired.
- Renewal succeeds but nothing reloads. A new certificate is on disk, and the web server or load balancer is still serving the old one from memory.
- The wrong certificate is served somewhere. One node in a pool, one CDN edge, or one rarely used hostname still has the old file.
- Someone else owns it. A vendor endpoint, a partner API or an appliance with a manually installed certificate, where you cannot see the renewal process at all.
Notice that in the third and fourth cases the certificate on disk is fine. Only a check against the live endpoint, doing the same handshake a client does, sees what clients see.
Check the served certificate, not the file
The most reliable signal is the expiry date of the certificate that the endpoint presents right now. You can read it yourself:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -enddate
Two details matter. Pass -servername, or a server hosting several sites may hand you a different site's default certificate through SNI. And check every hostname that matters, not only the apex: www, api and the status or login hostnames are usually separate certificates or at least separate configuration.
A one-off command proves nothing about next month, so the real task is to run the same check on a schedule and alert on the number of days remaining.
Choosing a warning threshold
The threshold has to be longer than the longest delay between "someone is told" and "a new certificate is live and reloaded". Work it out from your own process rather than copying a number.
- Fully automated, 90-day certificates. Renewal normally happens around 30 days before expiry, so a healthy certificate should never be seen with fewer than about 30 days left. A warning at 21 days means "the renewal that should have happened did not". That leaves three weeks, plenty for a person to look.
- Manual or ticketed renewal. Add the time to get a purchase approved, validation completed and a change window. Two to four weeks is a common floor; use more if renewals need a vendor.
- Certificates you do not control. Warn early, to someone who can contact the owner, because your fix is an email, not a command.
Use two stages. The first is a low-urgency note, such as a ticket or a chat message, with weeks of slack. The second, a few days before expiry, should be a real page, because by then the automatic path has clearly failed and the cost of waiting is an outage. A single alert at seven days tends to be seen on a Friday and fixed on a Monday that never comes.
Who should receive it
Expiry warnings are the classic alert that nobody owns. They fire rarely, so people forget where they go. Send the early stage somewhere with an owner, a named person or a team queue, and make sure that destination survives staff changes. Avoid a shared mailbox that nobody reads: the warning is only useful if a human reads it within days.
The late stage should follow your normal escalation path, since it is now an incident in waiting. If your on-call process can be reached for an outage, it can be reached for "this will be an outage on Thursday".
Doing this in Hesklo
Every HTTPS monitor in Hesklo tracks its certificate expiry as part of the normal check, so there is no separate monitor to create. A branch node can test whether the certificate expires within a number of days you choose and route the flow to whichever notification you want. That makes the two-stage setup above two branches on one monitor. The details are on the SSL monitoring page, and the canvas that holds the branches is described under escalation policies.
A checklist
- List every hostname clients connect to, including vendor and partner endpoints you depend on.
- Check the served certificate for each one, with SNI, not the file on disk.
- Find out how each renews and who owns it. Write the owner down.
- Set a first warning longer than your slowest renewal path, and a second a few days before expiry.
- Route the first to a named owner and the second to whoever is on call.
- After any change to DNS, load balancers, CDNs or the renewal client, confirm the served expiry date moved.
- Once a year, deliberately let a low-risk test certificate approach your warning threshold and confirm the alert arrives.
The last item is the one most teams skip. An alert that has never fired is an assumption, and an expiry warning is exactly the kind you rarely see in practice. Test it while nothing depends on the answer.