# CAA records explained: RFC 8659, accounturi, validationmethods

> How DNS CAA records limit which CAs may issue for your domain: issue, issuewild, iodef, tree climbing, and RFC 8657 accounturi and validationmethods.

Source: https://ssl.sh/learn/caa-records/
Last updated: 2026-11-10

A CAA (Certification Authority Authorization) record is a DNS record that tells certificate authorities which of them may issue for your domain. Every publicly trusted CA must check CAA before issuing a TLS certificate and must refuse if the record does not authorize it. [RFC 8659](https://www.rfc-editor.org/rfc/rfc8659) is the current specification, and [RFC 8657](https://www.rfc-editor.org/rfc/rfc8657) adds ACME account and validation-method restrictions.

## What CAA does and does not do

CAA is a control that CAs enforce on themselves at issuance time. It reduces the number of CAs whose validation bugs or social engineering could produce a certificate for your name from "every trusted CA" to the few you list.

CAA has clear limits:

- **Browsers ignore it.** A browser never checks CAA, so a mis-issued certificate from a CA that ignored your record is still accepted until it is revoked. For detection, monitor [Certificate Transparency](https://ssl.sh/learn/certificate-transparency/).
- **It applies only at issuance.** Existing certificates are unaffected when you add or change a record.
- **It is not authentication.** CAA restricts which CA may issue; the CA still has to validate that the requester controls the domain through [domain control validation](https://ssl.sh/glossary/dcv/).

CAA checking became mandatory on September 8, 2017 through CA/Browser Forum Ballot 187. The [Baseline Requirements](https://ssl.sh/glossary/baseline-requirements/) now require it for every name in a TLS certificate, and since Ballot SC-067 the lookup must also be corroborated from multiple network perspectives.

## Record format

Every CAA record has three parts: a flags byte, a tag, and a value.

```dns
example.com.  3600  IN  CAA  0 issue "letsencrypt.org"
;                             | |     |
;                         flags tag   value: CA domain [; parameters]
```

| Tag | Purpose | Defined in |
|---|---|---|
| `issue` | Authorizes a CA to issue any certificate for this name, including wildcards unless `issuewild` is present | RFC 8659 |
| `issuewild` | Authorizes a CA to issue wildcard certificates; when present it replaces `issue` for wildcards | RFC 8659 |
| `iodef` | Where a CA may report a refused request, as `mailto:` or `https:` URL | RFC 8659 |
| `issuemail` | Authorizes a CA to issue S/MIME certificates for email addresses at this domain | RFC 9495 |

The flags value is almost always `0`. Setting `128` marks the record **critical**: a CA that does not understand the tag must refuse to issue. Use it only when an unknown tag really must block issuance.

An empty CA domain forbids issuance. `0 issue ";"` means no CA may issue for the name, which is useful for domains that should never have certificates, such as parked domains or mail-only subdomains.

## How CAs find the relevant record set

A CA looks for CAA records starting at the exact name in the certificate and climbs toward the root, stopping at the first name that has any CAA records. This is called tree climbing:

```text
Request for: shop.eu.example.com
  1. shop.eu.example.com   CAA?  none
  2. eu.example.com        CAA?  none
  3. example.com           CAA?  0 issue "letsencrypt.org"   <- relevant record set, stop
  (com and the root are not consulted once a record is found)
```

Three consequences follow. First, one record at the registered domain covers all subdomains. Second, a record on a subdomain completely overrides the parent; the sets are not merged. Third, CAA lookups follow CNAMEs like any DNS query, so if `blog.example.com` is a CNAME to a hosting provider, the provider's CAA records apply to that name.

For wildcards the climb starts at the base name: for `*.example.com` the CA looks at `example.com`.

## issue versus issuewild

The `issuewild` tag exists so that you can allow ordinary certificates more widely than wildcards, or the opposite. The decision is easy to get wrong, so here is how a CA decides:

| Records at the relevant name | Non-wildcard request | Wildcard request |
|---|---|---|
| `issue "ca-a.example"` only | ca-a may issue | ca-a may issue |
| `issue "ca-a.example"` + `issuewild "ca-b.example"` | ca-a only | ca-b only |
| `issue "ca-a.example"` + `issuewild ";"` | ca-a may issue | nobody |
| `issuewild "ca-b.example"` only | any CA | ca-b only |
| `issue ";"` only | nobody | nobody |

Multiple records with the same tag are additive. Two `issue` records for two CAs authorize both.

## accounturi and validationmethods

RFC 8657 lets you narrow an authorization from "this CA" to "this account at this CA, using these methods". Both parameters follow the CA domain, separated by semicolons, and both are supported by Let's Encrypt.

```dns
; Only my Let's Encrypt account may issue, and only via dns-01
example.com. IN CAA 0 issue "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1234567890; validationmethods=dns-01"
```

- **accounturi** binds issuance to one ACME account URL. Someone who temporarily hijacks your web server or DNS but does not hold your ACME account key cannot obtain a certificate. Let's Encrypt account URIs look like `https://acme-v02.api.letsencrypt.org/acme/acct/` followed by the numeric account ID; your ACME client shows it after registration.
- **validationmethods** limits the challenge types the CA may use. Valid values for ACME are the challenge names: `http-01`, `dns-01` and `tls-alpn-01`. Restricting to `dns-01` blocks an attacker who controls only your web server.

These parameters are powerful and easy to break. Staging and production use different account URLs, a server rebuild may register a new account, and several teams may use separate accounts. Add one `issue` record per legitimate account rather than replacing the only one. See the [ACME protocol](https://ssl.sh/learn/acme-protocol/) for how accounts and challenges work.

## CA identifiers

The value in an `issue` record is the CA's own identifying domain, not the brand you bought from. Resellers sell certificates from a handful of issuing CAs, so check the issuing CA's documentation or the Issuer of an existing certificate. ACME CAs also publish the value as `caaIdentities` in their directory.

| CA | CAA value |
|---|---|
| [Let's Encrypt](https://ssl.sh/cas/lets-encrypt/) | `letsencrypt.org` |
| [Google Trust Services](https://ssl.sh/cas/google-trust-services/) | `pki.goog` |
| [DigiCert](https://ssl.sh/cas/digicert/) | `digicert.com` |
| [Sectigo](https://ssl.sh/cas/sectigo/) | `sectigo.com` |
| [GlobalSign](https://ssl.sh/cas/globalsign/) | `globalsign.com` |
| [SSL.com](https://ssl.sh/cas/ssl-com/) | `ssl.com` |
| [Amazon](https://ssl.sh/cas/amazon/) | `amazon.com` |

Some CAs accept several identifiers for historical brands, and ZeroSSL certificates are issued under Sectigo. When in doubt, confirm with the CA before adding a restrictive record.

## Example zone and lookup

A typical configuration authorizes one primary CA, a backup CA, and reporting. In a BIND-style zone file:

```dns
$ORIGIN example.com.
@        3600 IN CAA 0 issue "letsencrypt.org"
@        3600 IN CAA 0 issue "pki.goog"
@        3600 IN CAA 0 issuewild "letsencrypt.org"
@        3600 IN CAA 0 iodef "mailto:security@example.com"
; api is hosted on a cloud platform whose certificates come from Amazon
api      3600 IN CAA 0 issue "amazon.com"
; this name must never have a certificate
legacy   3600 IN CAA 0 issue ";"
```

Query the records with `dig`:

```bash
dig +short CAA example.com
```

```text
0 issue "letsencrypt.org"
0 issue "pki.goog"
0 issuewild "letsencrypt.org"
0 iodef "mailto:security@example.com"
```

For `api.example.com` the CA stops at the record set on `api` itself and never consults `example.com`. To see what a CA will actually use for a given name, walk up the labels yourself or use the [CAA checker](https://ssl.sh/tools/caa-checker/), which performs the same climb. The [CAA generator](https://ssl.sh/tools/caa-generator/) builds records with correct quoting for common DNS providers.

Keeping a backup CA in the record is a practical choice: if your main CA has an outage or a policy change, you can switch without waiting for a DNS change to propagate.

## DNS errors and DNSSEC

A CAA lookup that fails is not the same as a lookup that returns no records. If the authoritative server returns SERVFAIL, times out, or answers CAA queries with NOTIMP, the CA cannot tell whether a forbidding record exists and will usually refuse. The Baseline Requirements allow a CA to treat a lookup failure as permission only if the failure is outside its infrastructure, it retried at least once, and the zone has no DNSSEC validation chain to the root.

Let's Encrypt's documentation lists the usual culprits:

- DNSSEC signing bugs that only appear for empty responses, which CAA answers often are.
- Name servers that return NOTIMP for unknown query types instead of an empty NOERROR answer, which violates RFC 1035.
- Firewalls in front of name servers that drop queries for unfamiliar record types.

Your DNS provider does not need a CAA editor to be compatible; it only has to answer CAA queries correctly with an empty response.

## CAA rechecks and timing

CAs check CAA close to the moment of issuance, not only when validating the domain. The Baseline Requirements require a CA to issue within 8 hours of its CAA check, or within the record's TTL if that is longer, which is why Let's Encrypt limits authorization reuse to 7 hours in its `tlsserver` and `shortlived` profiles to avoid separate rechecks. Practically, a CAA change takes effect at your next order once DNS caches expire, so lower the TTL before a migration.

## Rolling out CAA safely

The safe way to introduce CAA is to inventory first and restrict second. A record that forgets one legitimate issuer breaks that issuer's next renewal, often weeks later, when nobody remembers the DNS change.

1. **Inventory.** Search Certificate Transparency logs for every certificate issued for your domain and its subdomains in the past year, for example with [CT search](https://ssl.sh/tools/ct-search/). Note each issuing CA, including those used by CDNs, email providers, status pages and other SaaS tools.
2. **Publish a permissive record.** List every CA you found with `issue`, and add `iodef` so that CAs that support reporting can tell you about refused requests.
3. **Override where needed.** Give subdomains hosted by third parties their own CAA set instead of widening the parent.
4. **Tighten gradually.** Add `issuewild`, `accounturi` or `validationmethods` one step at a time, and trigger a renewal on staging after each change.
5. **Re-check periodically.** New vendors arrive; repeat the CT inventory every quarter.

## Common mistakes

Most CAA failures come from forgetting that the record applies to every subdomain and every certificate source. Review this list before publishing a restrictive record:

| Mistake | Effect | Fix |
|---|---|---|
| Listing the reseller's brand instead of the issuing CA | Commercial order fails at issuance | Use the issuing CA's identifier |
| Forgetting a CDN or SaaS that issues certificates for a subdomain | That platform's renewal fails | Add its CA, or a CAA set on that subdomain |
| `issuewild` added without wildcard plans | Wildcard renewals blocked | Remove it or list the right CA |
| accounturi with only the staging or old account | Production renewal fails | Add a record per account |
| Typo in quotes or semicolons | Record ignored or rejected by the panel | Generate with a tool and verify with `dig` |

When an issuance fails because of CAA, the ACME error type is `caa` and the message names the domain; [CAA record prevents issuance](https://ssl.sh/errors/caa-record-prevents-issuance/) walks through the fix.
