CAA: certificate authority authorization
Who is allowed to mint TLS certificates for this name? CAA records let a domain restrict which CAs may issue certificates for it. Every publicly trusted CA is required to check and honor CAA before issuing (CA/Browser Forum Baseline Requirements §3.2.2.8): if your CAA doesn't list them, they refuse the request.
| Type number | 257 |
| RFC | 8659 (obsoletes 6844) |
| RDATA | flags · tag (issue / issuewild / iodef) · value |
What it holds#
Three components:
| Field | Purpose |
|---|---|
| Flags | A bitfield; 128 (high bit) marks a tag as critical: CAs that don't understand the tag must refuse to issue. 0 for everything else. |
| Tag | One of issue, issuewild, or iodef. |
| Value | A quoted string: typically a CA's domain ("letsencrypt.org") for issue/issuewild, or a mailto:/http(s): URL for iodef. |
The three tags#
example.com. CAA 0 issue "letsencrypt.org"
example.com. CAA 0 issuewild "letsencrypt.org"
example.com. CAA 0 iodef "mailto:security@example.com"Let's Encrypt is used here as the canonical example because it's free, widely supported, and well-documented, but any CA's domain works the same way. (Let's Encrypt's CAA notes cover their specific identifier and account-binding parameters.)
issue "ca.example": the named CA may issue any cert.issuewild "ca.example": the named CA may issue wildcards. Without this tag, the CA applies theissuepolicy to wildcard requests too (RFC 8659 §4.3).issue ";": issuance prohibited entirely (a lone;means "no issuer domain": this is the deny form RFC 8659's examples use). Useful to lock down subdomains.iodef "mailto:...": where the CA should send reports of misissuance attempts. Crucial to actually receive these: see "Gotchas."
When to use it#
- Always, at the apex. CAA is one of the cheapest hardening steps in DNS.
- At any name where you want a different policy than the parent: e.g., disable wildcard issuance for production while allowing it for staging.
When not to use it#
- Hard to think of a case. The only real cost is making sure you don't accidentally lock out a CA you actually use.
dig example#
Start with the demo record at caa.example.isitdns.net: a minimal, stable single-record case. (These records live directly inside the isitdns.net zone; example.isitdns.net is not a delegated zone of its own.)
dig @1.1.1.1 caa.example.isitdns.net CAA +short0 issue "letsencrypt.org"nslookup -type=CAA caa.example.isitdns.net 1.1.1.1Server: 1.1.1.1
Address: 1.1.1.1#53
Non-authoritative answer:
caa.example.isitdns.net rdata_257 = 0 issue "letsencrypt.org"A real apex often carries many more records. letsencrypt.org is a good real-world example because the domain itself publishes a full multi-CA policy:
dig @1.1.1.1 letsencrypt.org CAA +short0 issue "amazon.com"
0 issue "letsencrypt.org"
0 issue "pki.goog"
0 issue "sectigo.com"
0 issue "ssl.com"
0 issue "www.digicert.com"
0 issuewild "amazon.com"
0 issuewild "letsencrypt.org"
0 issuewild "pki.goog"
0 issuewild "sectigo.com"
0 issuewild "ssl.com"
0 issuewild "www.digicert.com"nslookup -type=CAA letsencrypt.org 1.1.1.1Server: 1.1.1.1
Address: 1.1.1.1#53
Non-authoritative answer:
letsencrypt.org rdata_257 = 0 issue "amazon.com"
letsencrypt.org rdata_257 = 0 issue "letsencrypt.org"
letsencrypt.org rdata_257 = 0 issue "pki.goog"
letsencrypt.org rdata_257 = 0 issue "sectigo.com"
letsencrypt.org rdata_257 = 0 issue "ssl.com"
letsencrypt.org rdata_257 = 0 issue "www.digicert.com"
letsencrypt.org rdata_257 = 0 issuewild "amazon.com"
letsencrypt.org rdata_257 = 0 issuewild "letsencrypt.org"
letsencrypt.org rdata_257 = 0 issuewild "pki.goog"
letsencrypt.org rdata_257 = 0 issuewild "sectigo.com"
letsencrypt.org rdata_257 = 0 issuewild "ssl.com"
letsencrypt.org rdata_257 = 0 issuewild "www.digicert.com"nslookup prints CAA records in
rdata_257 = ...format because it has no CAA-specific formatter, even in current BIND builds, so it falls back to the generic numeric-type output. The data is correct; the label is just CAA's type number, 257.
Many managed DNS providers auto-inject CAA records for the CAs backing their edge TLS certificates. These show up in dig even though you never added them in the dashboard. Cloudflare's behavior is documented at developers.cloudflare.com/ssl/edge-certificates/caa-records: when a zone uses Universal SSL or advanced certificates and you publish any CAA record of your own, Cloudflare adds records for its certificate partners alongside yours. The currently documented set is letsencrypt.org, pki.goog (Google Trust Services), sectigo.com (Sectigo, formerly Comodo CA), and ssl.com; Cloudflare notes the list is not exhaustive and can change. Older injected sets can linger: the isitdns.net zone queried below still carries comodoca.com and digicert.com entries from an earlier Cloudflare partner list.
DoH query (HTTPS)#
DNS-over-HTTPS (DoH) sends queries over HTTPS to a resolver that accepts them on port 443. You can query a CAA record over DoH using curl with the JSON API:
curl -s -H 'accept: application/dns-json' \
'https://cloudflare-dns.com/dns-query?name=caa.example.isitdns.net&type=CAA'{
"Status": 0,
"TC": false,
"RD": true,
"RA": true,
"AD": true,
"CD": false,
"Question": [{"name": "caa.example.isitdns.net", "type": 257}],
"Answer": [{
"name": "caa.example.isitdns.net",
"type": 257,
"TTL": 300,
"data": "\\# 22 00 05 69 73 73 75 65 6c 65 74 73 65 6e 63 72 79 70 74 2e 6f 72 67"
}]
}(Formatted for readability; the wire response is one line. The TTL counts down from 300 if the resolver answers from cache.) Cloudflare's JSON API returns CAA in wire-format hex because it has no CAA decoder: the hex 00 05 69 73 73 75 65 6c 65 74 73 65 6e 63 72 79 70 74 2e 6f 72 67 decodes to flags=0, tag=issue (length 5), value=letsencrypt.org. Google's JSON API decodes it for you: the same query at https://dns.google/resolve?name=caa.example.isitdns.net&type=CAA returns "data": "0 issue \"letsencrypt.org\"". The AD: true field confirms the record was DNSSEC-validated by the resolver.
nslookupcan't do DoH/DoT/DoQ. On Windows 11,Resolve-DnsNameuses the system-configured DoH resolver when the OS is set up for encrypted DNS; it does not accept a-DnsOverHttpsparameter or let you specify an arbitrary DoH endpoint. To probe a specific DoH service, use thecurlform above.
DNSSEC#
CAA records at isitdns.net are signed. A validating resolver returns AD=1 (authenticated data), proof the CAA RRset checked out against the zone's DNSSEC chain and was not tampered with on the way to the resolver. CAs run their own validating lookups when they check CAA, which is what actually blocks an attacker from spoofing the record at issuance time.
dig @1.1.1.1 isitdns.net CAA +dnssec +short0 issue "comodoca.com"
0 issue "digicert.com; cansignhttpexchanges=yes"
0 issue "letsencrypt.org"
0 issue "pki.goog; cansignhttpexchanges=yes"
0 issue "sectigo.com"
0 issue "ssl.com"
0 issuewild "comodoca.com"
0 issuewild "digicert.com; cansignhttpexchanges=yes"
0 issuewild "letsencrypt.org"
0 issuewild "pki.goog; cansignhttpexchanges=yes"
0 issuewild "sectigo.com"
0 issuewild "ssl.com"
CAA 13 2 300 20260718115640 20260716095640 34505 isitdns.net. ...The trailing CAA 13 2 ... line is the RRSIG covering the CAA RRset: CAA is the type it covers, 13 is the signing algorithm (ECDSAP256SHA256), 2 is the label count, 300 the original TTL, then the signature expiration and inception timestamps, the key tag, the signer, and the signature itself (truncated here). Cloudflare re-signs on a rolling window of a few days, so the two timestamps and the signature change constantly: yours will differ. For DNSSEC to help with CAA, the zone must be fully signed AND have a DS record at the parent (.net). isitdns.net meets both conditions. This matters more than ever: since 2026-03-15 the Baseline Requirements require CAs to DNSSEC-validate all CAA lookups back to the IANA root trust anchor, and to treat validation failures as "do not issue" rather than permission. See dnssec for how the chain of trust works.
nslookup can't show the AD bit or RRSIG records. Use
digor thecurlDoH approach above.
Gotchas#
- CAA on a CNAME target also applies. RFC 8659 §3 leaves CNAME processing to normal DNS resolution, so a CAA query for a CNAMEd name returns the target's CAA records: the policy on the target zone counts. Surprising if a subdomain CNAMEs out to a SaaS host: the SaaS's CAA may be missing the CA you wanted to use. (If the target has no CAA at all, the CA climbs the parents of the original name, not the target's: RFC 8659 dropped RFC 6844's CNAME tree-climbing.)
issuewildoverridesissuefor wildcards when present. A loneissue "letsencrypt.org"record authorizes that CA to issue both non-wildcard and wildcard certs (RFC 8659 §4.3). Once you add anyissuewildrecord, CAs fall back toissuewildfor wildcard requests: a CA listed only inissuecan no longer issue wildcards. If you publishissuewildrecords, every CA you want to issue wildcards must appear there explicitly.- Missing
iodefdefeats half the point. Without aniodefclause, misissuance attempts have no return address: you'll never know a wrong CA tried to issue for you. Include one, but treat it as best effort: the Baseline Requirements only say CAs SHOULD dispatch reports to theiodefcontact and are "not required to act on the contents of the iodef property tag", and CAs are only expected to supportmailto:andhttps:URLs there. Many CAs never send reports at all. - Auto-injection by managed DNS providers. Many managed DNS providers auto-inject CAA records for the CAs backing their edge TLS certificates. These show up in
digeven though they're not in your dashboard: it's documented behavior (see the Cloudflare example above), not a compromise. Don't be alarmed. - Lock yourself out carefully. Adding
issue ";"at the apex prohibits issuance for the whole zone, and renewals will silently fail. Test on a subdomain first. - TTLs matter, but with an 8-hour floor. The Baseline Requirements let a CA that has processed your CAA record issue "within the TTL of the CAA record, or 8 hours, whichever is greater". So a 24h TTL means a CAA fix can take a day to reach CAs, while cutting the TTL below 8h buys nothing on the CA side. 1h is still a fine default (resolver caches clear faster); just expect up to 8 hours before every CA sees a policy change.
See also#
- CNAME: the chain-walking behavior CAA inherits
- DNSSEC: signing CAA so a resolver can verify the CA list was not tampered with
- TLSA: pinning the certificate itself in DNS, a step beyond CAA
- DoH and DoT: querying DNS over encrypted transports
- The CAA lesson: full walkthrough with examples
- All record types: cheat sheet
- Query types: how resolvers ask for specific record types