SSL Certificate Expiry Is A Calendar Problem, Not A Security Problem.

We read the certificate expiry on six domains we run. All healthy. The failure worth monitoring is renewal stopping, which is silent for weeks.

Aditya Sharma·10 min read

Short answer: ssl certificate expiry almost never happens because somebody forgot to renew. It happens because automatic renewal stopped working weeks earlier and produced no signal at all. The certificate stays valid the whole time, so nothing looks wrong until the morning it does. The theory only holds if the trend actually behaves the way it is supposed to, which is worth confirming rather than assuming once the automation is presumed healthy.

What healthy looks like

Table of certificate expiry for six domains, ranging from 42 to 87 days remaining, all issued by Google Trust Services, with expiry dates from October to December 2026
Read directly from each TLS handshake. Nothing here needs attention, which is the point

of looking. You only learn what normal looks like by measuring when nothing is wrong.*

DomainDays remainingExpiresIssuer
posimyth.com874 Dec 2026Google Trust Services
protuno.com7521 Nov 2026Google Trust Services
cms.protuno.com7521 Nov 2026Google Trust Services
theplusaddons.com7420 Nov 2026Google Trust Services
uichemy.com6410 Nov 2026Google Trust Services
nexterwp.com4219 Oct 2026Google Trust Services

Caveat: six domains, all ours, read on one day. A single healthy reading proves nothing about renewal; that is the entire argument of this post. The useful version of this table is the same six domains read again in a fortnight, to see whether the numbers moved the way they should.

Modern certificates are short-lived by design, so a portfolio spread across 42 to 87 days at any given moment is completely ordinary. The number itself is not the alert. The alert is a number that stops going up.

A domain sitting at exactly 30 days, then 29, then 28, is a renewal that is simply not happening. A domain that jumps back up to somewhere near 90 has renewed successfully. That trend is the actual signal, and a single reading cannot show it to you on its own, no matter how healthy it looks.

Why renewal stops, and why SSL certificate expiry surprises people

Table of five renewal failures: the renewal timer disabled during unrelated work, HTTP-01 validation blocked by a later firewall or redirect rule, DNS moved so DNS-01 credentials fail, a removed subdomain breaking a multi-domain renewal, and renewal emails going to a departed employee. None are visible before expiry
Every one of these leaves a valid certificate right up until it does not. The automation

fails, and the artefact it produced stays good for weeks.*

FailureVisible before expiry?Detail
renewal cron or timer disablednooften during unrelated server work
HTTP-01 validation blocked by a rulenoa firewall or redirect rule blocks /.well-known/acme-challenge/
DNS moved, DNS-01 now failsnothe API credential still points at the old provider
a subdomain was removed from DNSnothe whole multi-domain renewal fails on one name
renewal emails go to a departed employeenothe warnings are delivered to nobody

The fourth row catches people who did everything right. A certificate covering five subdomains renews as a unit. Remove one of those subdomains from DNS, as part of a perfectly reasonable tidy-up, and the whole renewal fails because that name can no longer be validated. The other four sites are fine, then all five expire together.

This is the same failure shape as a scheduled job that stopped months ago: automation that produces nothing when it succeeds, and therefore nothing when it fails.

The second reading, exactly as the caveat above asked for

The caveat under the opening table says a single healthy reading proves nothing about renewal, and that the useful version of that table is the same six domains read again later. So I read them again while expanding this post, rather than let that caveat sit as an unfulfilled promise.

for d in posimyth.com protuno.com cms.protuno.com theplusaddons.com uichemy.com nexterwp.com; do
  echo | openssl s_client -servername "$d" -connect "$d:443" 2>/dev/null \
    | openssl x509 -noout -enddate
done
DomainDays remaining (first reading)Days remaining (this reading)
posimyth.com8784
protuno.com7571
cms.protuno.com7571
theplusaddons.com7470
uichemy.com6460
nexterwp.com4238

Every domain’s count dropped by roughly the number of days that actually passed between the two readings, no more and no less. That is what the trend this post argues for is supposed to look like: a number counting down at the rate a calendar counts down, not stuck, not jumping backwards unexpectedly, not frozen at the same figure twice. None of these six are anywhere near their renewal window yet, most ACME clients renew around 30 days before expiry, so a flat 403 on validation right now would not show up as a stalled count for weeks. nexterwp.com, now at 38 days, is the one to read again first: it is the closest of the six to the point where a renewal attempt should actually fire, and it is the domain this two-reading check would catch first if something has gone wrong with it. Whichever domain in a real portfolio sits lowest at any given check is always the next one worth a third reading before the other five.

I also checked chain validity and hostname coverage again while I had the connection open, rather than only re-read the expiry date:

echo | openssl s_client -servername theplusaddons.com -connect theplusaddons.com:443 \
  -verify_return_error 2>&1 | grep -i "verify return code"
Verify return code: 0 (ok)

Clean on all five external domains checked, matching the state this post originally reported and confirming nothing has quietly regressed in the time between the two readings. And the hostname coverage, read from each certificate’s Subject Alternative Name extension directly:

protuno.com:        DNS:protuno.com, DNS:*.protuno.com
theplusaddons.com:   DNS:theplusaddons.com, DNS:*.theplusaddons.com
nexterwp.com:        DNS:nexterwp.com, DNS:*.nexterwp.com

Every one of them uses a wildcard covering the bare domain and every subdomain in one certificate, which closes off the specific example.com-but-not-www.example.com gap this post warns about further down. That is a deliberate, sensible choice for a portfolio this size: one certificate per domain, one renewal event to watch per domain, rather than a narrower certificate per subdomain multiplying the number of renewal events that could each fail independently.

Checking it

Terminal loop reading the certificate end date for each domain via openssl s_client, followed by a chain verification command returning Verify return code 0
The second command is the one people skip. A server sending its own certificate but

omitting the intermediate works in most desktop browsers, which cache intermediates, and fails on Android and on API clients.*

That incomplete-chain case deserves its own mention because it is genuinely hard to diagnose from a desk. The site works perfectly on your laptop. A customer on an Android phone gets a security warning. Both are true, and openssl reports it in one line.

Why the multi-domain failure mode does not actually apply to our own six

The fourth failure in the table above, one subdomain removed from DNS taking down renewal for four unrelated ones, assumes a single certificate spans multiple distinct hostnames bundled together. Reading the actual Subject Alternative Name field on our own six domains showed that is not the shape of this particular portfolio, and it is worth being specific about why rather than let the failure mode read as universally applicable.

protuno.com:        DNS:protuno.com, DNS:*.protuno.com

Two entries: the bare domain and a wildcard for everything under it. cms.protuno.com is covered by that same wildcard rather than needing its own separate SAN entry, which is why it shares an identical expiry date with protuno.com in both readings above, same certificate, same renewal event, not a coincidence. A wildcard structured this way means removing some unrelated subdomain from DNS, the exact scenario the failure table describes, has no bearing on this certificate’s renewal at all, because nothing outside *.protuno.com and protuno.com itself is part of it to begin with.

The trade-off worth naming honestly: a wildcard is simpler to renew and immune to the specific multi-domain failure this post warns about, but it is also a single point of failure of a different kind. Every subdomain under a wildcard-covered domain shares one certificate, so a compromise or misissuance affecting that one certificate affects every subdomain simultaneously, which a set of narrower, per-subdomain certificates would not. Neither structure is categorically safer. They fail differently, and knowing which one a given domain actually uses, checked directly rather than assumed, is what tells you which failure mode is worth planning for on that specific site.

What a change in issuer would actually tell you

All six domains in the original table share one issuer, Google Trust Services, which is consistent with a common automated renewal path across the whole portfolio rather than six independently configured setups. That consistency is itself a signal worth watching for, in the opposite direction from everything else in this post: not a number that stops moving, but an issuer field that suddenly changes on one domain while the rest stay put.

A legitimate reason exists for an issuer to change, a deliberate migration to a different CA or a hosting provider switch that brings its own certificate automation with it. An illegitimate one exists too: a certificate issued by an unexpected authority is exactly what a DNS-01 hijack or a misconfigured, unintended ACME account would produce, silently, with the site continuing to serve HTTPS throughout and nothing about the connection itself looking broken to a visitor. Reading the issuer field costs nothing extra during the same check that already reads the expiry date, and a portfolio where every domain has shared one issuer for months is the kind of baseline that makes a single unexplained change worth a phone call rather than something that blends into normal drift.

What to actually monitor

Three things, and only the first is about the date.

Days remaining, tracked over time. Alert when the number stops increasing after a renewal was due, not merely when it drops below a threshold. A threshold alert at 14 days gives you two weeks; a trend alert gives you sixty.

Chain validity, on every domain, because it fails independently of expiry. It belongs in the same pass as the other TLS and header checks, since all of them are read from the same connection.

Hostname coverage. A certificate valid for example.com but not www.example.com is valid and still breaks half your traffic. Check both, along with any subdomain that serves real requests.

And fix the notification address while you are in there. Renewal warnings sent to somebody who left are the same problem as domain expiry warnings sent to an address on the expiring domain: a notification system that cannot deliver its one important message.

Protuno’s free audit reads certificate expiry, chain validity and hostname coverage from the domain alone, which is how the table above was produced. Straight with you as on every post here: Wren, the Care agent that would track the trend rather than a single reading, is built and named but not live yet.

Run the loop across your portfolio and write the numbers down with today’s date. Do it again in a fortnight. The second reading is the one that tells you something.

Track four fields per domain each time, not one: days remaining, chain validity, hostname coverage, and issuer. The first is where the trend lives. The other three rarely change, which is exactly why a change in any of them, checked against a stable baseline built from a portfolio you have already read more than once, is worth noticing immediately rather than lost in a column of numbers nobody is comparing against last time.

Comments