acme.sh implements ACME Renewal Information (RFC 9773) automatically. No flag, no opt-in, no configuration needed.
If your CA exposes a renewalInfo endpoint in its ACME directory (Let's Encrypt, ZeroSSL, Sectigo, SSL.com, …), acme.sh will use it. If the CA does not, acme.sh falls back to the classic 30-day fixed-interval rule — behavior is identical to before.
What it does
| What | When | Why |
|---|---|---|
🔍 Polls suggestedWindow |
Every cron run, before deciding to skip | Lets the CA shift renewal forward in case of an incident (key compromise, mass revocation) |
| 🎯 Picks a random renewal time inside the window | Right after a successful issuance/renewal | Disperses renewals across the network so all clients don't hit the CA at the same instant |
🔗 Sends replaces=<certID> in newOrder |
On --renew |
Lets the CA correlate the new order with the certificate it supersedes (RFC 9773 §5) |
↩️ Retries without replaces |
If the CA returns alreadyReplaced or an ARI validation error |
Robust against edge cases (switching CAs, retired issuers, parallel renewal) |
When does the cert renew?
acme.sh renews when any one of these is true:
--forceis given- The CA's ARI
suggestedWindow.starthas passed (so the CA is asking you to renew now) - The cached
Le_NextRenewTimehas passed (the classic 30-day fallback, also used when no ARI)
This means a CA can effectively command an early renewal across the entire acme.sh user base by shrinking the suggested window — useful for emergency incident response.
Why this matters
Short-lived certificates (Apple's 47-day proposal, Let's Encrypt's shortlived profile, etc.) are coming. With shorter lifetimes, fixed-interval renewal becomes brittle:
- A 7-day cert with a 30-day fixed renewal interval would always be expired
- All clients renewing at the same offset would hammer the CA
- Emergency revocations need a way to push the renewal time forward
ARI solves all three. The CA gets full control of when to renew; clients just follow its hints.
How to inspect
The chosen next-renewal time is saved in the domain conf as Le_NextRenewTimeStr:
acme.sh --info -d example.com
# Look for: Le_NextRenewTimeStr=...
To see the live ARI window the CA is currently advertising, run with --debug 2:
acme.sh --renew -d example.com --debug 2 2>&1 | grep -i 'ARI suggestedWindow'
Example output:
[INFO] ARI suggestedWindow: 2026-05-06T19:48:34Z to 2026-05-08T14:59:23Z
[INFO] Next renewal time picked from ARI window: 2026-05-07T16:57:49Z
Internals
- certID for ARI requests is computed per RFC 9773 §4.1 as
base64url(AKI) + "." + base64url(Serial)of the existing certificate. - Window pick is
start + (current_epoch % window_size). This is intentionally pseudo-random across clients (different cert issuance moments → different offsets) without needing crypto-grade randomness, and it is the same simple "use current time as entropy" pattern acme.sh already uses for cron randomization. - Failure handling: any failure mode (ARI 404, network error, malformed response, CA does not support ARI) cleanly falls back to the original fixed-interval
Le_NextRenewTimelogic. ARI is a strict enhancement.
Relationship to other features
- Compatible with all validation modes: webroot, standalone, alpn, DNS API, DNS manual, DNS persist. ARI only changes when renewals happen, not how they validate.
- Compatible with
--valid-to/--valid-from(Validity): when an explicit notAfter is set, acme.sh respects the user-provided deadline and does not let ARI override it. - Compatible with
--cert-profile(Profile selection): independent.
CAs known to support ARI
- ✅ Let's Encrypt
- ✅ ZeroSSL (when ACME server returns
renewalInfo) - Check your CA's directory — if it has a
"renewalInfo"field, ARI is on.
curl -s https://acme-v02.api.letsencrypt.org/directory | grep renewalInfo
Spec reference
Full normative reference: RFC 9773 — Automated Certificate Management Environment (ACME) Renewal Information (ARI) Extension
Key sections:
- §4.1 — certID computation
- §4.2 —
suggestedWindowshape - §4.3 — polling and
Retry-After - §5 — newOrder
replacesfield - §7.4 —
alreadyReplacederror code