Your CAA record decides whether ACME failover works
ACME failover to a second CA only works if your CAA record already authorizes that CA. Here is how the backup issuer gets refused on the day you need it, what changes when every CA must honor accounturi from 15 March 2027, and how to test it now.ACME failover only works if your CAA record already authorizes the backup CA. The fallback CA checks CAA at issuance time like any other CA, and if its identifier is missing from your issue (or issuewild) set, it refuses. That refusal never shows up on a normal day. It shows up when your primary CA is down or rate-limiting you, which is the only day the backup was supposed to matter.
A second CA you cannot issue from is not redundancy. It is a config line that makes you feel covered. This post covers how the refusal happens, how account pinning makes it stricter from 15 March 2027, and how to test the backup path before you need it.
How a CAA record refuses a fallback CA
Every publicly trusted CA looks up CAA for the requested name before issuing. If the zone has any issue records and none of them names that CA, issuance stops. Let's Encrypt's implementation is short enough to read in full. This is the line in Boulder's va/caa.go that produces the error your ACME client receives:
if !valid {
return berrors.CAAError("CAA record for %s prevents issuance", foundAt)
}On the wire, that becomes an ACME problem of type urn:ietf:params:acme:error:caa, one of the error types RFC 8555 defines. Other CAs word the detail differently, but the type is the same.
Now combine that with how fallback is configured. Caddy's automatic HTTPS docs describe the default: "If Caddy cannot get a certificate from Let's Encrypt, it will try with ZeroSSL; if both fail, it will backoff and retry again later." After all issuers fail, Caddy backs off exponentially, with a maximum of one day between attempts, for up to 30 days. If your CAA set lists only letsencrypt.org, the ZeroSSL attempt is refused every time, and the retry loop runs against a policy that will never pass.
Caddy is the case that makes this concrete, because failover is its default. Its docs call it "the first (and so far only) server to support fully-redundant, automatic failover to other CAs", which is Caddy's own claim. The other common clients bind a certificate to one ACME server: certbot's --server flag, lego's --server (a single value defaulting to Let's Encrypt production), and cert-manager's spec.issuerRef. With those, "failover" is a runbook or a script someone wrote. The CAA problem is identical, it only surfaces later: when a human switches the directory URL during an incident and the new CA refuses.
Find the exact identifier in the CA's ACME directory
The identifier you put in CAA is not always the brand name. ZeroSSL issues through Sectigo, so zerossl.com is not what it checks. The authoritative answer is in the meta.caaIdentities field of each CA's ACME directory, which RFC 8555 defines for exactly this purpose. Captured on 5 October 2026 (trimmed to the meta objects):
$ curl -s https://acme.zerossl.com/v2/DV90
"meta": {
"termsOfService": "https://www.sectigo.com/uploads/legal/Certificate-Subscriber-Agreement-3.0-click.pdf",
"website": "https://zerossl.com",
"caaIdentities": ["sectigo.com", "trust-provider.com", "usertrust.com", "comodoca.com", "comodo.com", "entrust.net", "affirmtrust.com"],
"externalAccountRequired": true
}
$ curl -s https://acme-v02.api.letsencrypt.org/directory
"meta": {
"caaIdentities": [
"letsencrypt.org"
],
$ curl -s https://dv.acme-v02.api.pki.goog/directory
"meta":{"termsOfService":"https://pki.goog/GTS-SA.pdf","website":"https://pki.goog","caaIdentities":["pki.goog"],"externalAccountRequired":true,Any one of ZeroSSL's identities authorizes it. Sectigo's own CAA page lists sectigo.com, trust-provider.com and usertrust.com, so sectigo.com is the safest choice. Note externalAccountRequired: true on ZeroSSL and Google Trust Services too: both require external account binding, whether you supply the credentials or your client fetches them. Register the backup account before the incident, not during it.
Matching is exact. Boulder compares the record's domain to its own with caaDomain == issuerDomain, so a near-miss spelling is the same as no record at all.
Wildcards check a different list
The second common gap is issuewild. RFC 8659 says that "if at least one issuewild Property is specified in the Relevant RRset for a Wildcard Domain Name, each issue Property MUST be ignored" for that request. Add your backup CA to issue, forget issuewild, and the backup works for www.example.com but not for *.example.com.
Real zones show the two lists differing. GitHub's CAA set, queried on 5 October 2026:
$ dig +noall +answer CAA github.com
github.com. 3600 IN CAA 0 issue "digicert.com"
github.com. 3600 IN CAA 0 issue "globalsign.com"
github.com. 3600 IN CAA 0 issue "letsencrypt.org"
github.com. 3600 IN CAA 0 issue "sectigo.com"
github.com. 3600 IN CAA 0 issuewild "digicert.com"
github.com. 3600 IN CAA 0 issuewild "letsencrypt.org"
github.com. 3600 IN CAA 0 issuewild "sectigo.com"GlobalSign may issue github.com but not a wildcard under it. That may well be deliberate there. In your zone, the same shape after adding a backup CA in a hurry is usually an oversight, and it only bites the wildcard renewal.
Two more places the relevant record can live: CAA is inherited from the parent when a name has none of its own, and Cloudflare's CAA documentation notes that "CAA records on a CNAME target also apply". Check every level with dig, not only the apex.
Account pinning makes the backup line stricter in March 2027
RFC 8657 adds two CAA parameters. accounturi restricts a CA line to one ACME account (the URL of the account object), and validationmethods restricts which challenge types may be used. The point of accounturi is that someone who briefly hijacks your DNS or web server still cannot issue without your account key.
Until now, processing these parameters was optional for CAs. CA/Browser Forum Ballot SC098v2 changes that: it "adds the requirement that CAs process the Certification Authority Authorization (CAA) parameters defined in RFC 8657", and "these requirements take effect on March 15, 2027." It passed with 22 of 23 voting certificate issuers in favor, and Apple, Google and Mozilla voted yes on the browser side. The ballot's own discussion summary is candid about why: the value "will only be realized if CAs process the parameters rather than ignoring them."
Let's Encrypt already enforces both parameters, which is visible in the Boulder code above. For each other CA, assume nothing before March 2027 unless its documentation says otherwise. After that date, a pin on any CA line is a hard restriction, and that has three consequences for a failover setup.
Pin the primary, leave the backup open, and the backup is the attack path
If letsencrypt.org is pinned and sectigo.com is not, an attacker with temporary control of your HTTP or DNS can register a fresh ZeroSSL account and get a certificate. The pin on the primary bought nothing. A pinned setup is only as strict as its least-pinned line.
A backup account you have not created has no URI to pin
Pinning the backup means registering the backup account in advance and recording its URL, which your client received in the Location header when it registered (RFC 8555 section 7.3). Let's Encrypt's account URLs look like https://acme-v02.api.letsencrypt.org/acme/acct/1234567890. A setup that creates the fallback account lazily, at the moment it first needs it, cannot be pinned, because the account does not exist until the incident.
Lost account storage now breaks both CA lines at once
RFC 8657 is blunt: "A Property with an invalid or unrecognized 'accounturi' parameter is unsatisfiable." Boulder compares the pinned URI against the requesting account ID exactly. Rebuild a container without its persistent volume, the client registers new accounts, and every pinned line now refuses. Caddy's docs say the $HOME folder "must be writeable and persistent" for a reason. With pins on both lines, losing that folder takes out the primary and the fallback at once, which is the opposite of what the fallback was for.
A failover-safe CAA set, with placeholder account IDs:
example.com. 3600 IN CAA 0 issue "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1234567890"
example.com. 3600 IN CAA 0 issue "sectigo.com; accounturi=<your ZeroSSL account URL>"
example.com. 3600 IN CAA 0 issuewild "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1234567890"
example.com. 3600 IN CAA 0 issuewild "sectigo.com; accounturi=<your ZeroSSL account URL>"
example.com. 3600 IN CAA 0 iodef "mailto:security@example.com"Each line carries one accounturi. RFC 8657 makes a property with two of them unsatisfiable, and naming a ZeroSSL account on the letsencrypt.org line is unsatisfiable too. If you are not ready to keep account storage durable, do not pin at all. An unpinned two-CA set that issues is better than a pinned one that fails closed.
Cloudflare's CA is the next identifier you will need
On 29 September 2026, Cloudflare announced its intent to become a public CA, positioned as a new source of free certificates that anyone on an existing free CA can switch to "by changing a directory URL". It is "not issuing certificates yet". The only date in the announcement, the first quarter of 2027, is for Merkle Tree Certificates specifically. It will also "only issue to clients that support ACME Renewal Information (ARI)", which we covered in how ACME Renewal Information works.
The announcement does not mention CAA, and no identifier is published. Cloudflare is acquiring a root from GlobalSign, but do not assume globalsign.com in your CAA will cover Cloudflare-issued certificates. When the ACME directory goes live, read its meta.caaIdentities and add that value. Until then there is nothing correct to add.
Cloudflare already runs the failover pattern this post describes for its own customers: every Universal SSL certificate "ships with a backup certificate, wrapped with a separate key and issued from a different authority". The CAA side is handled for you. Cloudflare "adds CAA records automatically when you have Universal SSL and add any CAA records to your zone", and those records "will not appear in the Cloudflare dashboard". This does not apply to Advanced Certificate Manager certificates. Two consequences: your dashboard understates what your zone publishes, and an ACM zone with a hand-written CAA set has no safety net.
Test the backup path before you need it
CAA climbs the DNS tree: a name with no CAA records of its own uses its parent's. A throwaway subdomain therefore exercises your apex policy, which makes a real issuance test cheap. With acme.sh, whose default CA has been ZeroSSL since v3.0:
acme.sh --register-account -m ops@example.com --server zerossl
acme.sh --issue -d caa-test.example.com --dns dns_cf --server zerosslIf that fails with a CAA error, your failover would have failed the same way. The issued test certificate lands in public CT logs, so pick a subdomain name you do not mind publishing. If you pin accounturi, run the test from the account your fallback actually uses, or it fails for the wrong reason. Run it again after every CAA change, and once more after March 2027 if you add pins. Avoid certbot's --dry-run for this: its docs warn that with a custom --server it "may cause real certificates to be issued and discarded".
A checklist for the CAA side of failover:
- Every CA your client may fall back to appears in
issue- , using an identifier from that CA's
meta.caaIdentities- .
- If any
issuewild- record exists, every fallback CA appears there too.
- Pins are all-or-nothing: either every CA line carries an
accounturi- or you accept the unpinned one as your real security level.
- Account storage for every pinned account survives a rebuild.
- Any
validationmethods- restriction allows the challenge type the backup client actually uses.
- A test issuance from the backup CA has succeeded since the last CAA change.
Adding CAs has a cost. Each line widens the set of organizations that can issue for you, which is the opposite of what CAA was designed for. One primary and one tested backup is a better trade than five identifiers copied from a large company's zone.
Watching CAA and issuers over time
The failure mode here is slow: someone tightens the CAA set in spring, and the effect appears at a renewal months later. A daily diff of CAA records catches that change when it happens. Detecting DNS record changes shows how to build that loop with dig. WebPixie's DNS monitoring checks CAA daily from the authoritative server and shows the previous and new value side by side when it changes.
On the certificate side, Certificate Transparency logs record which CA issued each certificate, so a switch to the backup issuer (or your test issuance) is visible there. Building a certificate inventory from CT logs covers doing this by hand, and the Certificates Manager lists the issuer for each certificate seen in CT logs for your domains. If both CAs refuse, SSL monitoring warns ahead of expiry. It does not issue or renew certificates, so the fix is still yours. If the primary failure was rate limits rather than an outage, Let's Encrypt rate limits in 2026 explains which limit you hit.