Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Protocol Support

acme-proxy implements RFC 8555 in full, plus the extensions an ACME client in 2026 expects to find. This page is the conformance summary: what a client can call, which RFC section governs it, and what is deliberately not implemented.

Everything here is served per profile, under /profile/<name>/. There is no ACME at the bare root — see Profiles & Routing.

Resources

Every row is reachable at {base_url}/profile/<name><path>. The Advertised column says whether the directory object names it: a client is expected to find advertised resources by reading the directory rather than by constructing paths.

PathMethodRFCAdvertisedNotes
/directoryGET, POST§7.1.1—It is the entry point; §6.3 requires POST-as-GET to work too.
/newNonceHEAD, GET, POST§7.2yesAll three forms.
/newAccountPOST§7.3yesFind-or-create by public key: 201 for a new account, 200 for an existing one, Location either way.
/acct/{id}POST§7.3.2, §7.3.6—Contact update and deactivation. kid-authenticated. There is deliberately no unauthenticated GET.
/acct/{id}/ordersPOST§7.1.2.1—The account’s order list, filtered as §7.1.2.1 requires.
/keyChangePOST§7.3.5yesKey Rollover.
/newOrderPOST§7.4yesAccepts notBefore/notAfter and RFC 9773’s replaces.
/order/{id}POST§7.1.3—POST-as-GET.
/order/{id}/finalizePOST§7.4—Takes the CSR; hands it to the configured signer.
/authz/{id}POST§7.5, §7.5.2—One URL serves both the read and the deactivation, told apart by whether a payload arrived.
/chall/{id}POST§7.5.1—Triggers validation. Both outcomes are 200.
/certificate/{id}POST§7.4.2—POST-as-GET; PEM chain.
/revokeCertPOST§7.6yesRevocation & CRL.
/renewalInfo/{certID}GETRFC 9773 §4.1yesUnauthenticated. Advertised without the id — §4.1 has the client append it.
/crlGETRFC 5280noRouted but not advertised: a CRL is CA infrastructure, not an ACME resource.
/ca.pemGET—noThe profile’s trust anchor, so installing it is one curl. 404 unless the backend has one of its own. Not advertised, for the same reason as /crl. See Trusting the CA.

Two paths sit outside every profile, on the root router:

PathPurpose
/healthLiveness. Outside the filter chain, the admission limiter and the nonce middleware — see Monitoring.
/.well-known/acme-challenge/{token}Mounted only when a signer backend has an http-01 token store to publish, i.e. signer.relay.challenge_strategy = "http01". See Relay.

Two further paths are not on this listener at all. GET /metrics has a socket of its own (off by default, [metrics]), so firewalling that port is what controls who can read it — see Monitoring. The web admin’s /ui and /api likewise have their own listener; see Web Admin.

Protocol behaviour worth knowing

Every signed request is checked the same way. The media type (application/jose+json), any crit header, the JWS url against the route actually reached, and the nonce are all verified before a handler runs, so no resource can forget one. jwk and kid are mutually exclusive per §6.2, and the verification algorithm never rests on the client’s alg alone. See Architecture.

Refusals are problem documents. Every rejection is application/problem+json with an RFC 8555 URN type. A multi-identifier order rejected on several names comes back as one compound problem with a subproblems entry per identifier (§6.7.1); a single rejection stays its own type, unwrapped.

A POST always carries a fresh Replay-Nonce, errors included (§6.5). GET /directory, GET /crl, GET /ca.pem and GET /renewalInfo do not mint one — nothing asks them to, and each nonce is a committed database write.

Extensions

ExtensionRFCDefaultPage
External Account Binding§7.3.4offEAB
Account key rollover§7.3.5always onKey Rollover
Renewal Information (ARI)RFC 9773always onARI
Terms of service§7.3.3offSet meta.terms_of_service and newAccount starts enforcing it.
Wildcard identifiers§7.1.3requires dns-01Challenge Validation

Directory metadata

The directory’s meta object carries only what is configured. An unset member is omitted, never sent empty — "website": "" says less than saying nothing.

  • externalAccountRequired appears as true when EAB is on for that profile.
  • termsOfService, website and caaIdentities come from [meta].

meta.terms_of_service is the one with teeth: setting it turns on §7.3.3, so newAccount then refuses a request without termsOfServiceAgreed: true (403 userActionRequired plus a Link: rel="terms-of-service" header), and the account object starts reflecting the field.

Not implemented

Stated explicitly, because each is something a reader may reasonably expect:

  • CAA checking. meta.caa_identities is advertised to clients; this server does no CAA lookup of its own. Where the relay backend is in use, the upstream CA performs its own.
  • OCSP. Revocation is published as a CRL at GET /crl. There is no OCSP responder, and no authorityInfoAccess OCSP pointer is ever written into an issued certificate. The local CA does write the caIssuers half of that extension, and a cRLDistributionPoints pointer, once an operator names the URLs — see Local CA.
  • Identifier types other than dns. newOrder accepts DNS names, including wildcards; ip identifiers (RFC 8738) and permanent-identifier are not supported.
  • Pre-authorization (§7.4.1). The directory does not advertise newAuthz, and it is not routed. Authorizations exist only as part of an order.
  • POST to /renewalInfo — RFC 9773 §4.3’s optional client-side renewal signal. The GET half is implemented.