CAA records: who can issue certs for your domain
A Certification Authority Authorization record is two lines of DNS that tell the world's Certificate Authorities: "only these CAs are allowed to issue certs for my domain. If a request comes in to anyone else, refuse it." Per RFC 8659 (CAB Forum baseline mandatory since September 2017), every CA MUST check CAA before issuing.
Five minutes to deploy, near-zero maintenance, real protection against rogue cert issuance. Yet only ~30% of domains we scan have one. Let's fix that.
The threat without CAA
A CA's job is to verify domain control before issuing a cert. Verification is usually a DNS challenge or HTTP-01 challenge. Both are robust under normal circumstances, but they fail under three scenarios:
- Compromised CA — DigiNotar 2011, Symantec 2017. The CA itself is breached and issues certs without authorisation.
- Compromised registrar / DNS provider — attacker briefly modifies your DNS to pass DNS-01 with a CA you don't normally use.
- BGP hijack — attacker reroutes traffic to your IP for the few seconds needed to pass HTTP-01.
In all three cases, an attacker can mint a valid cert for your domain from any compliant CA worldwide. CAA limits this to ONE CA — the one you actually use. Attacks now require compromising YOUR specific CA, which dramatically narrows the threat model.
The record format
example.com. IN CAA <flag> <tag> "<value>"
The three tags that matter:
issue— authorises a CA to issue end-entity certs (your normal HTTPS cert).issuewild— authorises a CA to issue wildcard certs (*.example.com).iodef— email/URL the CA contacts when an unauthorised request is rejected.
The flag is almost always 0 (advisory). 128 is the "critical" bit — if the CA doesn't understand the tag, it must refuse — but in practice not used.
The 3-line config that covers 95% of domains
example.com. IN CAA 0 issue "letsencrypt.org"
example.com. IN CAA 0 issuewild "letsencrypt.org"
example.com. IN CAA 0 iodef "mailto:security@example.com"
"Only Let's Encrypt may issue certs for example.com or any subdomain wildcard. Notify
security@ if anyone else tries." Replace letsencrypt.org with your CA of
choice. The standard CA identifiers:
| CA | identifier |
|---|---|
| Let's Encrypt | letsencrypt.org |
| Google Trust Services | pki.goog |
| DigiCert | digicert.com |
| Sectigo | sectigo.com |
| GlobalSign | globalsign.com |
| Amazon Trust Services | amazontrust.com |
| Microsoft (Azure) | microsoft.com |
If you use multiple CAs, list each on its own row. Order doesn't matter.
Provider-specific notation
OVH, Cloudflare, Gandi etc. all expect form fields rather than BIND zone syntax. Mapping:
| Field | Value |
|---|---|
| Type | CAA |
| Sub-domain (Name) | blank (apex), or the subdomain |
| Flags | 0 |
| Tag | issue (or issuewild / iodef) |
| Value | letsencrypt.org (or the chosen CA) |
Verifying with dig
$ dig +short CAA example.com
0 issue "letsencrypt.org"
0 issuewild "letsencrypt.org"
0 iodef "mailto:security@example.com"
TTL on CAA records is typically 24h. Changes propagate within that window. ACME issuance fetches CAA at request time, so an updated CAA blocks new issuance immediately.
Subdomain inheritance
CAA is inherited from parent zones (until overridden). A CAA at example.com
covers www.example.com, api.example.com, etc. unless
api.example.com has its own CAA. Useful pattern: tight CAA at apex, looser
CAA on a specific subdomain that uses a different CA.
What can go wrong
- Forgetting
issuewild. If you haveissue letsencrypt.orgonly and try to renew a wildcard, LE refuses. Addissuewildtoo if you use wildcards. - Account binding. Some setups bind CAA to a specific ACME account:
0 issue "letsencrypt.org;accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/12345". Stronger but requires syncing the account URI. - Pointless quotes / typos. Validate with
dig CAAafter publishing."letsencrypt.org "with trailing space breaks issuance silently.
UnveilScan's check
Our dns checker (Basic profile) emits dns.no_caa as MEDIUM
when no CAA record is present at the apex. The Extended profile additionally validates
the CAs listed against the actual issuers in your CT logs — if your CAA says "letsencrypt.org
only" but crt.sh shows a Sectigo cert from last month, that's a CAA misconfiguration or
a forgotten cert.
Check your CAA in 60 seconds
Free Basic scan reports CAA presence + recommends the canonical 3-line snippet.
Scan a domain