We treat certificate renewal as a solved problem. You wire certbot renew to a timer, a fresh certificate lands thirty days before expiry, and nobody gets paged. I believed that for years too.
The other day I was digging through letsencrypt.log on my own server looking for something else entirely, and this line stopped me:
"renewalInfo": "https://acme-v02.api.letsencrypt.org/acme/renewal-info",
That line is Let's Encrypt saying: I can tell you when your certificates should be renewed — ask me here. It appears seven times in my log, once per directory fetch. So I checked whether anything had ever actually gone to that address. Zero requests. For years the CA has been holding out an address, and my client has never once looked at it.
This post is about what that address is, why it suddenly matters this year, and how to find out whether your own setup uses it.
Where does "thirty days" come from?
The honest answer: nowhere in particular. Let's Encrypt certificates last 90 days, a third of 90 is 30, and clients renew when a third of the lifetime remains. It's a reasonable rule — but it's the client's rule. The party that actually signed the certificate has no say in it.
For a single certificate that looks harmless. Across millions, it cracks in two places.
First, clustering. If everyone renews at the one-third mark, certificates issued together on one day all want renewing together sixty days later. What the CA sees isn't a smooth load curve; it's a crowd arriving in waves.
Second, and this is the real problem: revocation. When a CA discovers it has misissued a certificate, the rules force it to revoke within days. Your client, meanwhile, consults its calendar, decides it still has 46 days, and goes back to sleep.
Here the mental picture most of us carry — mine included — is out of date. You assume the browser will throw a warning and you'll notice. It may not. Let's Encrypt dropped OCSP URLs from its certificates on 7 May 2025 and shut off its OCSP responders entirely on 6 August 2025; revocation now travels only via CRLs. I can see this directly on my own server's certificate — the Authority Information Access extension carries nothing but CA Issuers, with no OCSP entry at all:
$ openssl x509 -in cert.pem -noout -text | grep -c -i OCSP
0
So your certificate may be revoked while your visitors' browsers go on not noticing for days. The reliable way to learn you've been revoked is no longer the browser's reaction — it's the signal the CA hands you directly. Black boxes tend to have their worst nights without telling anyone.
ARI exists to close exactly those two cracks.
What ARI actually does
ACME Renewal Information — ARI — was standardised as RFC 9773 in June 2025. One sentence covers the idea: it moves the renewal time from a constant inside the client to a signal published by the CA.
The mechanism is refreshingly plain. The CA advertises a renewalInfo field in its ACME directory object. For a given certificate, the client builds an identifier from the keyIdentifier in the certificate's Authority Key Identifier and its DER-encoded serial number — the value octets only, without the tag and length bytes — each base64url-encoded and joined by a period. Trailing = padding must be stripped from both halves; leave it on and you get a 404. Append the result to the renewalInfo URL and issue a GET.
The response is a two-field JSON object. suggestedWindow carries start and end timestamps describing the recommended renewal period. explanationURL is optional and points at a page explaining why the window looks the way it does — the RFC says clients SHOULD surface that URL to their operator. A Retry-After header tells the client how often to ask again.
The crucial detail comes next. The client doesn't take the start of the window; it picks a uniformly random moment from inside the window. That is precisely what solves the clustering problem: even when everyone receives the same interval, everyone renews on a different minute. If the chosen moment has already passed, renew now. If it hasn't, the RFC's preferred path is for the client to schedule itself for exactly that moment — that's the step that actually preserves the spread. A client that can't schedule itself has two options left: renew immediately if the moment falls before its next scheduled wake-up, and otherwise sleep for Retry-After and ask again.
When it renews, the client adds a replaces field to its newOrder request, declaring which certificate this one supersedes. The server is expected to validate it — the RFC's word is SHOULD — checking for the same account, at least one shared identifier, and a predecessor not already replaced. If it has been, you get HTTP 409 with an alreadyReplaced problem type. The RFC also offers a piece of advice: if the CA doesn't advertise renewalInfo in its directory, the client should not send replaces at all. That's a SHOULD NOT rather than an outright ban, and the RFC gives no reason for it; my own guess is that a server meeting an unfamiliar field may reject the order outright.
There's a quiet trap too: a window whose end equals or precedes its start is invalid. Clients must treat that as a failed request and fall back to their normal schedule. So when ARI itself misbehaves, it doesn't leave you unrenewed — it degrades to the old behaviour. That's a well-designed fallback.
Why this got urgent this year
ARI became a standard in 2025, but the reason I'm writing about it this week is the calendar.
The CA/Browser Forum's SC-081v3 ballot passed in April 2025 and set a schedule that walks certificate lifetimes down in stages. From the compliance-dates table in the Baseline Requirements:
| Date | Max certificate lifetime | Domain validation data reuse |
|---|---|---|
| 15 March 2026 | 200 days | 200 days |
| 15 March 2027 | 100 days | 100 days |
| 15 March 2029 | 47 days | 10 days |
The first step has already landed — six months ago, as I write this. And notice the column that tightens hardest is the right-hand one: by 2029 you may reuse validation data for just 10 days. Renewal doesn't merely get more frequent; nearly every renewal will require revalidation.
Now apply "renew at one third remaining" to 47 days: you're left with a 15-day buffer. A single DNS provider outage landing over a long public holiday can eat the entire buffer. Let's Encrypt's own integration guide already accounts for this maths, recommending that certificates shorter than 10 days be renewed at half their lifetime rather than a third.
Looking at that table, here's my read: keeping the renewal date as a client-side constant becomes a slightly worse bet with every reduction in lifetime. ARI moves that date into the control plane — the party issuing the certificate knows more about it than you do. I've argued before that certificates should be treated as a platform component rather than a file-refresh chore; ARI is that argument expressed at the protocol level.
Does your setup use ARI?
Let me leave the theory and come back to my own server, because the answer surprised me.
My VPS holds 21 Let's Encrypt certificates, all renewed by certbot.timer, without a single failure in years. On Ubuntu 24.04 LTS the distribution package gives:
$ certbot --version
certbot 2.9.0
Per Certbot's changelog, ARI support arrived in 4.1.0 (10 June 2025), and the entry reads: "certbot renew will automatically check ARI when using an ACME server that supports it, and may renew early based on the ARI information." 5.0.0 (2 September 2025) then began persisting the Retry-After value so it survives across runs. The current release is 5.8.0.
I'm on 2.9.0. That gap isn't a patch level; it's two major versions.
Rather than assume, I checked. There is not one line of ARI code in the installed Python packages:
$ grep -rl "renewal_info\|renewalInfo" /usr/lib/python3/dist-packages/acme
(no output)
$ grep -c "GET .*/acme/renewal-info" /var/log/letsencrypt/letsencrypt.log
0
Those renewalInfo lines in the log never meant my client was asking for ARI. They were simply the directory object being dumped verbatim — sitting in the same block as newAccount, newNonce and newOrder. The CA has been pointing at a door for years, and the client has walked past it every single time.
If you run the distro package, this very likely applies to you as well. Distribution packages freeze versions for the life of an LTS; that's a sound policy, but when a new protocol capability appears it leaves you quietly outside. Quietly, because nothing breaks — the new thing simply never starts.
Three commands to check:
-
certbot --version— below 4.1.0 means no ARI. Nothing short of a snap or pip install fixes that. Mind the transition: install the snap without removing the distro package and two timers end up driving the same/etc/letsencryptdirectory. Stop and disablecertbot.timerfirst, then remove the apt package, then install the snap. - Fetch your CA's directory and look for the field:
curl -s https://acme-v02.api.letsencrypt.org/directory | grep renewalInfo - Search your renewal log for a real request to
/acme/renewal-info. Don't confuse it with the name in the directory dump — what you want is a GET to that address.
Certbot isn't the only client, of course. Caddy has supported ARI since v2.8.0 — but note the word in that release note: it says the ARI draft spec. The certificate identifier construction changed between drafts, so a deployment pinned to a 2024 build may not agree with today's RFC endpoint. Current Caddy releases have settled on the RFC format. Run a modern client and you're probably doing this already without knowing, which is exactly how ARI is supposed to behave.
Things to get right in production
Turning ARI on doesn't end at "upgrade the package". A few things deserve separate thought.
Polling frequency. Let's Encrypt's integration guide recommends checking ARI for each certificate at least twice a day. A once-daily cron sees an urgent window contraction half a day late. By definition, half a day is a long time during an emergency.
Don't delete the fallback. Once ARI works, removing the old schedule is tempting. Don't. If the CA is unreachable, the window comes back invalid, or something breaks upstream, the client needs a default to fall back on: a third of the lifetime at 90 days, half of it below 10.
Don't swallow explanationURL. It's the only human-readable clue about why a window narrowed. If your client doesn't even log it, then during a mass revocation event all you'll have is "certificate renewed early" — and by the time you want the reason, the incident is over. Put it in the alert text.
Monitor the signal, not the days. Most teams monitor certificates by days-to-expiry. Under ARI that metric turns misleading: a certificate may have 40 days left while the CA wants it replaced today. What you want to watch is whether the suggested window has been passed, not how many days remain. In normal times the two agree; the day they disagree is the day that matters.
In practice what you want to measure collapses to a single number: the gap between suggestedWindow.end and now. If it's negative, you're late. Add it as a second threshold next to your existing days-remaining alert — there's no need to delete the old one, it's your fallback anyway.
Plan for your own stampede. In a mass revocation event the CA pulls the window into the past for every affected certificate. On your side that means all 21 of my certificates would say "renew now" at the same instant. ARI's random-moment rule protects the CA, not you — think about a queue that paces your own renewals so you don't hit issuance rate limits. For anyone holding hundreds of certificates this isn't a theoretical worry.
If you use wildcards and DNS-01, read that table again. The right-hand column drops to 10 days in 2029. Wildcard certificates can only be issued via DNS-01, and reusing validation data for just 10 days means writing a fresh TXT record on effectively every renewal. If your DNS provider has no API, or your flow needs a human to approve the record, that flow stops working on that date. ARI doesn't fix this — but it tells you in advance when it will bite.
Does your internal CA speak ARI? If you run your own CA for internal services — I've written before about short-lived certificate automation with step-ca — your client's ARI expectations and your CA's published directory may not line up. Same rule as always: read the directory first.
Do the arithmetic on polling. Twice a day, per certificate. Twenty certificates is forty requests; five thousand is ten thousand. On large fleets, caching ARI responses for the duration of Retry-After is far more sensible than re-asking on every renewal attempt.
Don't send replaces blindly. The RFC is explicit: if the directory has no renewalInfo, leave the field out. If you're writing your own ACME client or talking to a private CA, read the directory first and decide afterwards.
Closing
You can read ARI as a small addition: one JSON endpoint, two timestamps, a random number. What it really does is transfer authority. The renewal date stops being a constant your client invented at install time and becomes a signal the issuer keeps updating.
As lifetimes fall towards 47 days, that transfer stops being optional. But the lesson I took from this wasn't about timing — it was about visibility. My automation had run flawlessly for years, and that is precisely why I never looked inside it. A system that never breaks can also be a system that never moves forward, and from the outside those two look identical.
Take a minute this evening: check your client's version, fetch your CA's directory, grep your log for that GET. Either it's already there and you can relax, or you'll discover that you, like me, have been walking past the door for years.
Top comments (0)