Skip to content
neil edited this page May 1, 2026 · 1 revision

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:

  1. --force is given
  2. The CA's ARI suggestedWindow.start has passed (so the CA is asking you to renew now)
  3. The cached Le_NextRenewTime has 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_NextRenewTime logic. 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 — suggestedWindow shape
  • §4.3 — polling and Retry-After
  • §5 — newOrder replaces field
  • §7.4 — alreadyReplaced error code

Clone this wiki locally