What does a CAA record do?
CAA is record type 257, defined in RFC 8659 (which obsoleted RFC 6844). It exists to close a structural weakness in the web PKI: any of the hundreds of trusted CAs can issue a certificate for any domain. CAA lets the domain owner publish a machine-readable allowlist, and the CA/Browser Forum Baseline Requirements make checking it mandatory before issuance. Unlike a TXT record, CAA has a real structured wire format — flags, tag, and value are separate fields, not a string you parse.
The enforcement point is the CA, not the browser. A browser does not look at CAA when validating a connection — by then the certificate exists and CAA is irrelevant. CAA is a control on the *issuance* process, which is exactly why it is useful: it stops an attacker who has compromised a domain-validation flow at some unrelated CA from getting a working certificate for your name.
CAA syntax and fields
| Field | Example | Meaning |
|---|---|---|
| Name (owner) | google.com. | The domain the policy applies to. CAA is inherited by subdomains via tree climbing. |
| TTL | 9310 | Cache lifetime. |
| Class / Type | IN CAA | Record type 257. |
| Flags | 0 | 8-bit field. Only bit 0 — the critical flag, value 128 — is defined. 0 is the normal value. |
| Tag | issue | Property name: issue, issuewild, or iodef. Case-insensitive. |
| Value | "pki.goog" | Quoted string. A CA domain for issue/issuewild, a URL for iodef. Optional key=value parameters follow a semicolon. |
The three tags
- `issue` — authorizes a CA to issue certificates for this domain.
0 issue "letsencrypt.org"permits Let's Encrypt. The special value0 issue ";"authorizes no CA, which is how you say "never issue certificates for this name". - `issuewild` — authorizes a CA specifically for wildcard certificates. If any
issuewildrecord is present, it governs wildcards entirely andissuerecords are ignored for wildcard requests. If noissuewildexists,issuecovers both. This asymmetry is the subtlest part of CAA. - `iodef` — where the CA should report a policy violation, as
mailto:or anhttps://endpoint. Reporting is best-effort and CAs vary in whether they send anything, so treat it as a bonus rather than a monitoring channel.
The value string is the CA's own CAA identifier — a specific domain string the CA publishes, not a guess. letsencrypt.org, pki.goog, digicert.com, sectigo.com, comodoca.com, ssl.com, amazon.com, and amazontrust.com are common ones, but the authoritative source is always the CA's current documentation. Getting the string wrong is functionally identical to not authorizing them.
The critical flag
The flags byte has one defined bit: bit 0, the issuer critical flag, written as the value 128. It means "if you do not understand this property tag, you must refuse to issue" — a fail-closed instruction for future tags a CA might not recognize.
For the three standard tags you should use 0. Setting 128 issue "letsencrypt.org" is legal and behaves the same way in practice for a CA that understands issue, but it adds no security and risks unexpected refusals. Real zones almost universally publish 0, as both examples above do. Reserve the critical flag for a case where you genuinely need unknown-tag handling to fail closed.
How CAs check: tree climbing
A CA asked to issue for shop.eu.example.com does not just query that exact name. It climbs:
- Query
shop.eu.example.comfor CAA. If a record set exists, that set is the policy — the search stops, and parent records are not consulted. - If empty, query
eu.example.com. Same rule. - Continue up to
example.com, and stop at the registrable domain boundary. - If no CAA record is found anywhere on the path, issuance is unrestricted.
Two consequences follow that catch people out. First, a CAA set at a subdomain completely overrides the parent — it does not merge. If example.com permits Let's Encrypt and api.example.com publishes 0 issue "digicert.com", then Let's Encrypt cannot issue for api.example.com. Second, CNAMEs are followed before the climb: if the name is an alias, the CA follows the CNAME and evaluates CAA at the target, which means a vendor's CAA policy can govern your subdomain.
Also note that CAA lookups happen against the live DNS at issuance time, and a SERVFAIL is not the same as an empty answer — under RFC 8659 a CA must treat a lookup failure as a reason not to issue (with narrow retry allowances). A broken DNSSEC signature on your zone can therefore block certificate renewal while the site itself still loads for non-validating resolvers. Check with the DNSSEC checker if renewals start failing for no visible reason.
A realistic policy
; Only Let's Encrypt may issue, including wildcards
example.com. 3600 IN CAA 0 issue "letsencrypt.org"
example.com. 3600 IN CAA 0 issuewild "letsencrypt.org"
example.com. 3600 IN CAA 0 iodef "mailto:[email protected]"
; A subdomain that uses a different CA — this OVERRIDES the parent entirely
api.example.com. 3600 IN CAA 0 issue "digicert.com"
; A name that should never have a certificate
internal.example.com. 3600 IN CAA 0 issue ";"Before you publish, enumerate every certificate you actually have — including ones a CDN, load balancer, or platform issues on your behalf — and make sure each issuing CA is listed. A CAA record that omits the CA your renewals depend on breaks nothing today and breaks everything in ninety days.
TTL guidance
3600 is a sensible default. CAA records are queried only at issuance, so query volume is negligible and there is no performance argument for either extreme. Do drop the TTL to 300 for the hour or two around a CA change — CAs may hold a cached answer, and you want a newly added authorization visible before you trigger the issuance. Raise it again afterwards.
Common CAA mistakes
- Locking out the CA you actually use. Publishing
0 issue "letsencrypt.org"while your CDN silently renews through a different CA means renewals begin failing at the next cycle, not immediately. Inventory first. - Forgetting `issuewild`. If you publish any
issuewildrecord, it alone governs wildcard requests. A policy of0 issuewild "digicert.com"with0 issue "letsencrypt.org"means Let's Encrypt cannot issue your wildcard. - Using the wrong identifier string.
letsencrypt.com,sectigo.net, or a marketing brand name instead of the published CAA identifier. Check the CA's current docs. - Including a scheme or path. The value is a bare domain —
"https://letsencrypt.org"is wrong. - Expecting CAA to revoke existing certificates. It does not. Adding CAA today has no effect on a certificate issued yesterday; that one remains valid until it expires or is revoked.
- Assuming it stops all mis-issuance. CAA binds compliant CAs. A CA that ignores it, or a compromised one, is not stopped by a DNS record. It raises the bar, it does not close the door.
- Setting the critical flag without reason.
128on a standard tag adds risk and no benefit. - A subdomain CAA that unintentionally overrides the parent. Remember the set at the closest name wins outright; there is no merging.
Check it with dig
# CAA policy for a domain
dig +short google.com CAA
# Full record set with TTLs
dig +noall +answer cloudflare.com CAA
# Walk the tree the way a CA does — the first non-empty set wins
dig +short shop.eu.example.com CAA
dig +short eu.example.com CAA
dig +short example.com CAA
# Straight from the authoritative server
dig +noall +answer @ns1.google.com google.com CAA
# What certificate is actually deployed, and who issued it?
openssl s_client -connect google.com:443 -servername google.com </dev/null 2>/dev/null \
| openssl x509 -noout -issuer -subject -datesCross-check the deployed certificate and chain with the SSL certificate checker, and read expiry dates from the command line using Check SSL expiry with OpenSSL. If a renewal has already failed and the certificate has lapsed, Fix ERR_CERT_DATE_INVALID covers the browser-side symptoms; an unrelated-looking browser error may instead be an incomplete chain.