CAA, the Forgotten DNS Record
Of the ~150 root CAs your browser trusts, exactly one needs to be willing to mis-issue a cert for your domain to compromise it. CAA records are how you tell the other 149 to refuse. They take five minutes to add, they're a hard requirement for every public CA, and almost nobody publishes them.
A CAA record is a DNS entry that names which Certificate Authorities are allowed to issue certificates for your domain. Every CA is required to check it before issuing. If your CAA record names only Let's Encrypt, no other CA in the world can produce a cert for you — even if an attacker has compromised them. Five-minute change, big payoff.
Why CAA exists
The TLS trust model has one very awkward property: every CA your browser trusts can issue a certificate for
every domain on the internet. There's no scoping. Verisign doesn't “own” certs for
verisign.com — technically, any CA can issue one. The only thing standing between you and a
rogue cert is the CA's internal validation process and their willingness to enforce it.
That's worked out about as well as you'd expect. In the last decade we've seen DigiNotar issue fraudulent Google certificates (and get distrusted, killing their business), Symantec mis-issue thousands of certs (also distrusted), and a long tail of smaller incidents at almost every major CA. Each incident is contained by the browsers eventually pulling trust, but in the window between mis-issuance and detection, the cert is real and works.
CAA was designed to give domain owners a way to opt out. The record published in your DNS says “only these specific CAs are authorized to issue for me.” Every CA in the world is required by the CA/Browser Forum Baseline Requirements (ยง3.2.2.8) to check CAA before issuing — and to refuse if they're not on the list.
Without CAA, any CA can issue. With CAA naming only Let's Encrypt, every other CA must refuse โ even if they're compromised, tricked, or hostile.
Anatomy of a CAA record
A CAA record has three parts: a flag, a tag, and a value. In zone file format:
yourdomain.com. 3600 IN CAA 0 issue "letsencrypt.org"
Breaking that down:
0— the flag byte. Almost always0. Setting it to128marks the record as critical — CAs that don't understand the tag must reject the request rather than fall back to a default. You don't need this for the standardissuetag.issue— the tag. The three you'll see:issue(regular certs),issuewild(wildcard certs specifically),iodef(where to report violations)."letsencrypt.org"— the value. Forissueandissuewild, this is the CA's identifier (a DNS-name-like string the CA chose for itself).
The three tags in detail
| Tag | What it controls |
|---|---|
issue | Which CAs may issue any certificate for this domain. If absent, any CA may issue. If present, only the named CAs may. |
issuewild | Which CAs may issue wildcard certificates specifically. If absent, the issue policy applies to wildcards too. If present, it overrides for wildcards only. |
iodef | Where to send an Incident Object Description Exchange Format report if a CA receives a request that violates the CAA policy. Most CAs ignore this; the ones that don't will email you. |
Copy-paste recipes
The most common patterns, ready to drop into your DNS provider's UI:
“Only Let's Encrypt” (most common)
yourdomain.com. 3600 IN CAA 0 issue "letsencrypt.org"
yourdomain.com. 3600 IN CAA 0 iodef "mailto:security@yourdomain.com"
The second line is optional but a good idea — if anyone ever tries to get a cert from another CA, you'll find out about it.
“Let's Encrypt for regular certs, DigiCert for wildcards”
yourdomain.com. 3600 IN CAA 0 issue "letsencrypt.org"
yourdomain.com. 3600 IN CAA 0 issuewild "digicert.com"
This is a common enterprise pattern — cheap automation-friendly Let's Encrypt for most of your hostnames, with an EV/wildcard cert from DigiCert (or similar) for the main marketing site.
“Nobody is allowed to issue”
yourdomain.com. 3600 IN CAA 0 issue ";"
The ; as value means “deny all”. Useful for parked domains, internal-only domains that
shouldn't ever have a public cert, or as a temporary lockdown after a security incident while you investigate.
Identifier strings for major CAs
| CA | CAA identifier |
|---|---|
| Let's Encrypt | letsencrypt.org |
| DigiCert | digicert.com |
| Sectigo (was Comodo) | sectigo.com (also accepts comodoca.com) |
| GlobalSign | globalsign.com |
| Amazon (ACM) | amazon.com, amazontrust.com, awstrust.com, amazonaws.com |
| Google Trust Services | pki.goog |
| Buypass | buypass.com |
| SSL.com | ssl.com |
| Entrust | entrust.net |
| Cloudflare (Universal SSL) | cloudflare.com (issues via DigiCert / Google / Let's Encrypt depending) |
Gotchas that catch teams out
Subdomain inheritance
CAA lookup walks up the DNS tree. If api.yourdomain.com doesn't have a CAA record, CAs check
yourdomain.com. If that doesn't have one, they check com (which is fine — no
record means “anyone may issue”).
This means setting CAA at the apex covers everything underneath automatically. But if you set a CAA record at
a subdomain, it completely replaces the apex policy for that subdomain — not adds to it. So if
you have a CAA at yourdomain.com naming Let's Encrypt, and you add a CAA at
api.yourdomain.com naming DigiCert, you've just removed Let's Encrypt's authorization for
api.yourdomain.com.
CDNs and CAA
If your site is behind Cloudflare, AWS CloudFront, or another CDN that provisions its own certs, you need that
provider's CAA identifier in your record. Cloudflare's docs are explicit about this: their CAA token is
cloudflare.com, but they may actually issue through several backend CAs (DigiCert, Let's Encrypt,
Google Trust Services), so the cleanest pattern is to allow Cloudflare and your backups:
yourdomain.com. 3600 IN CAA 0 issue "cloudflare.com"
yourdomain.com. 3600 IN CAA 0 issue "letsencrypt.org"
yourdomain.com. 3600 IN CAA 0 issue "pki.goog"
Renewals fail silently
The CAA check happens at every issuance, including renewals. If you change your CAA record to remove a CA, certs issued by that CA continue to work until they expire — but when renewal time comes, the renewal will fail. The failure mode is often a confused error message hours before your cert expires. Always update CAA first, watch for one renewal cycle to confirm things still work, then make further changes.
What FatDig shows you
The CAA Records card on the Advanced Dig lists every CAA entry for the domain. Each record shows:
- The tag (
issue/issuewild/iodef) as a coloured pill. - The value in monospace, with deny-all (
;) clearly labelled. - Whether the critical flag (128) is set, as a separate badge.
If the domain has no CAA records, the card says so explicitly — not as an error, but as a flag that any publicly-trusted CA may issue. That's the case for most domains on the internet right now, including some big ones that really should know better.
Try it on FatDig: dig github.com and check the CAA card — a well-considered policy naming multiple CAs with specific roles. Then dig your own domain. If the CAA card says “No restrictions”, you've got a five-minute task to do.