Guide · ACME
ACME Renewal Information (ARI): how renewal windows work
Checked against sources on 2026-09-25 · 5 min read
ACME Renewal Information, usually shortened to ARI, is an extension to the ACME protocol published as RFC 9773 in June 2025. It gives a certificate authority a way to tell your ACME client when to renew each certificate, instead of the client working it out from the expiry date.
That sounds minor. It matters in two situations. First, if a CA has to revoke a batch of certificates, it can pull the renewal window forward so clients replace them before the revocation lands. The RFC names mass revocation as one of the reasons ARI exists. Second, the CA can spread renewals over time, which Let's Encrypt says helps it avoid load spikes on its infrastructure.
The suggested renewal window
A CA that supports ARI adds a renewalInfo URL to its ACME directory. Let's Encrypt's production directory lists it like this:
"renewalInfo": "https://acme-v02.api.letsencrypt.org/acme/renewal-info"The client adds a certificate identifier to the end of that URL and sends an unauthenticated GET request. No account, no signature. The answer is a small JSON object:
{
"suggestedWindow": {
"start": "2026-11-02T17:18:36Z",
"end": "2026-11-04T12:29:25Z"
}
}That is a real response we got on 25 September 2026 for the certificate on letsencrypt.org itself, issued on 4 September 2026 and expiring on 3 December 2026. The window sits about 30 days before expiry, which lines up with Let's Encrypt's advice to renew when a third of the lifetime is left. A response may also include an explanationURL, and the RFC says clients should show it to their operator when it is there. If the window ever jumps weeks earlier than you expect, that link is where the CA explains why.
The response also carries a Retry-After header. In ARI this is not just a polite minimum: the RFC defines it as how long the client should wait before asking again. Ours said about six hours. Let's Encrypt's integration guide recommends checking ARI for each certificate at least twice a day.
What the client does with the window is also spelled out. It picks a random time inside the window. If that time is already in the past, it renews immediately. Otherwise it schedules the renewal for that time and keeps polling as Retry-After asks, in case the window moves.
The certID format
The identifier at the end of the URL is built from two fields of the certificate:
- The keyIdentifier from the certificate's Authority Key Identifier extension, base64url-encoded.
- The serial number, as its DER-encoded bytes without the tag and length, base64url-encoded.
Join the two with a dot and strip any trailing = padding from both. The example in the RFC comes out as aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE. The second part decodes to bytes starting with 00. DER adds that leading zero byte when the first byte of the serial has its high bit set, and the zero is part of the ID. Forget it and the CA will not recognise the certificate.
Here is a shell version that looks up the window for any certificate issued by Let's Encrypt. It needs openssl, xxd, base64 and curl, and we tested it against letsencrypt.org:
host=example.com
cert=$(echo | openssl s_client -connect "$host:443" -servername "$host" 2>/dev/null | openssl x509)
b64url() { xxd -r -p | base64 | tr '+/' '-_' | tr -d '=\n'; }
# Authority Key Identifier keyIdentifier, as hex
aki=$(echo "$cert" | openssl x509 -noout -ext authorityKeyIdentifier | tail -1 | tr -d ' :' | sed 's/^keyid//')
# Serial as DER content bytes: even length, leading 00 if the high bit is set
serial=$(echo "$cert" | openssl x509 -noout -serial | cut -d= -f2)
[ $(( ${#serial} % 2 )) -eq 1 ] && serial="0$serial"
case "$serial" in [89A-Fa-f]*) serial="00$serial";; esac
certid="$(echo "$aki" | b64url).$(echo "$serial" | b64url)"
curl -s "https://acme-v02.api.letsencrypt.org/acme/renewal-info/$certid"If the certificate came from another CA, Let's Encrypt replies with a malformed error saying the Authority Key Identifier did not match a known issuer. Use that CA's own directory instead, if it supports ARI.
The replaces field
ARI adds one more thing to the protocol. When a client orders the replacement certificate, it can include a replaces field in the new order, carrying the certID of the certificate it is replacing. That lets the CA link the new certificate to the old one and treat the order as a renewal rather than a brand new request. You never need to touch this yourself. It is the client's job, and a sign that the client really speaks ARI rather than only reading the window.
Two practical points follow from how the window works. Do not fetch it once and cache it for a month: the CA can move it, and the whole point is to notice when it does. And do not treat the end of the window as the expiry date. It is when the CA expects you to be done renewing, usually weeks before the certificate actually stops working.
Which clients support ARI
Here is what we could confirm from each project's own changelog or documentation:
| Client | ARI support | Source |
|---|---|---|
| certbot | Since 4.1.0 (10 June 2025): certbot renew checks ARI when the server supports it and may renew early. Since 5.0.0 it stores Retry-After between runs. | certbot changelog |
| lego | First implementation in v4.12.0 (May 2023). Since v4.20.2 it checks ARI unless you disable it. | lego changelog |
| Caddy | CertMagic, the library behind Caddy's automatic HTTPS, lists full support for RFC 9773. | CertMagic README |
We left other clients out because we could not confirm their status from a primary source. If yours is not listed, search its changelog for "ARI" or "renewalInfo". Old client versions will not have it, which is one more reason to keep them updated.
Using ARI to catch a missed renewal
ARI is written for clients, but it is just as useful to anyone watching from outside. The window tells you when the CA expects the certificate to have been replaced. If the end of the window has passed and the site still serves the same certificate, the renewal did not happen. That is a much earlier warning than "expires in 7 days", and it does not depend on guessing each client's renewal rule.
- Fetch the certificate the site serves, over TLS with the right server name.
- Build its certID and ask the issuing CA's renewalInfo endpoint for the window.
- If the current time is past the end of the window and the served serial has not changed, raise an alert.
- If the CA does not support ARI, fall back to the one-third rule: alert once less than a third of the lifetime is left.
This is how ExpiryOwl decides a certificate is renewal overdue: for Let's Encrypt certificates we read the ARI window, and for other ACME certificates we use the one-third rule. We check every certificate every six hours.
For a one-off look, our SSL checker shows a certificate's issuer and dates, and the 47-day readiness checker helps you judge whether a domain is ready for much shorter lifetimes. If you run infrastructure for many clients, the MSP page covers renewals across lots of tenants.
Why this matters more every year
The CA/Browser Forum is cutting the maximum certificate lifetime to 47 days by March 2029, and Let's Encrypt plans to issue 45-day certificates by default from February 2028. A 45-day certificate renewed with a third of its life left gives you 15 days of slack. In the same post Let's Encrypt says that renewing at a hardcoded 60-day interval will no longer be enough and recommends ARI. A client that follows the window renews on time. A monitor that reads the same window notices within hours when it does not.
Sources
- RFC 9773: ACME Renewal Information (ARI) Extension
- IETF Datatracker: RFC 9773
- Let's Encrypt production ACME directory
- Let's Encrypt: Integration guide
- Let's Encrypt: Improving resiliency and reliability with ARI
- Let's Encrypt: Decreasing certificate lifetimes to 45 days
- Certbot changelog
- lego changelog
- CertMagic README