toolhq.io

All posts
6 min readby Jameel Haider

CAA records explained: telling CAs who may issue for your domain

A CAA record is a DNS entry that names which certificate authorities are allowed to issue certificates for your domain. Every public CA is required to check it before issuing, and to refuse if the record exists and does not list them. It is one of the few controls that limits certificate misissuance at the source rather than detecting it afterwards, it takes one DNS record to deploy, and it is still missing from most domains.

example.com.  IN  CAA  0 issue "letsencrypt.org"
example.com.  IN  CAA  0 issuewild ";"
example.com.  IN  CAA  0 iodef "mailto:security@example.com"

You can check what a domain currently publishes with the DNS Lookup tool.

Why does this exist?

The public trust model has a structural weakness: any of the hundreds of trusted CAs can issue a certificate for any domain. A mistake, a compromise, or a coerced CA anywhere in that set produces a certificate for your domain that browsers accept, and you find out only if someone is watching Certificate Transparency logs.

CAA narrows the set. By publishing one record you turn "any CA may issue for example.com" into "only these may", and compliance is mandatory under the CA/Browser Forum baseline requirements, so a CA that ignores your record has committed a reportable violation. CAA is a preventive control; CT logs are the detective one. Both are worth having.

The syntax

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

<flags> <tag> "<value>"

Flags is almost always 0. The only defined bit is the critical flag (128), which tells a CA that if it does not understand the tag it must refuse to issue. Setting it on a tag a CA does not recognise blocks issuance entirely, so leave it at 0 unless you have a specific reason.

Tags, of which three matter:

TagMeaning
issueThis CA may issue certificates for this domain
issuewildThis CA may issue wildcard certificates
iodefWhere to report a violation attempt

The interaction between the first two is the part most often misread. issue alone covers both regular and wildcard issuance. Adding any issuewild record overrides that for wildcards specifically. So:

# Any CA listed may issue anything
example.com.  CAA  0 issue "letsencrypt.org"

# Let's Encrypt for normal certs, DigiCert for wildcards only
example.com.  CAA  0 issue "letsencrypt.org"
example.com.  CAA  0 issuewild "digicert.com"

# No wildcards at all, from anyone
example.com.  CAA  0 issuewild ";"

A semicolon as the value means "nobody". 0 issue ";" blocks all issuance for the domain, which is a legitimate setting for a domain that should never have a certificate, such as one used only for email or held defensively.

Values use the CA's own identifier domain, not their brand name. The common ones are letsencrypt.org, digicert.com, sectigo.com, globalsign.com, pki.goog for Google Trust Services, amazon.com and amazontrust.com for AWS Certificate Manager, and ssl.com. Get one wrong and you have not restricted anything, you have simply blocked the CA you use.

Some CAs support parameters after the value, notably account binding, which is far stricter:

example.com.  CAA  0 issue "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/12345"

That permits issuance only from your specific ACME account, so even someone who gains control of a host and runs their own client against the same CA cannot obtain a certificate.

How CAs walk the tree

When a CA is asked for a certificate for api.eu.example.com, it checks CAA at that exact name first. If no record exists, it strips one label and checks eu.example.com, then example.com, stopping at the first name that has any CAA record at all.

Two consequences follow. First, the closest record wins completely: a CAA set on a subdomain replaces rather than supplements the parent's, so a subdomain listing only digicert.com cannot be issued by the CA authorized at the apex. Second, an empty result at every level means unrestricted, which is the default state of most domains.

CNAMEs follow their target. If www.example.com is a CNAME, the CA follows it and evaluates CAA at the target name, which is a common surprise when a subdomain points at a platform whose own domain publishes a restrictive record. The mechanics of CNAME resolution are covered in A record vs CNAME.

Failure modes

  • You added CAA before checking every issuer. Your main certificate renews fine, then an unrelated system fails months later: a CDN provisioning its own certificate, a platform managing certificates for a custom domain, a mail provider issuing for mail.example.com. List every CA that issues on your behalf, not just the one you use directly.
  • DNSSEC is not required but is recommended. CAA is delivered over DNS, so an attacker able to spoof your DNS answers can hide the record from a CA. DNSSEC makes that forgery detectable, and pairing the two is the intended configuration.
  • The record is on the wrong name. CAA at www.example.com does nothing for example.com, since the lookup walks upward, not downward. Put the policy at the apex and add subdomain records only when the policy differs.
  • Old provider software. A few DNS hosts still lack native CAA support and require the generic record format. If yours cannot publish CAA at all, that is a reason to change providers.

Deploying it safely

  1. Enumerate every certificate currently valid for your domain using the CT log lookup. This is the ground truth on which CAs are actually issuing for you, including ones you forgot about.
  2. Publish a record listing all of them.
  3. Add iodef with a monitored address so violation reports reach a human.
  4. Verify with the DNS Lookup tool once the change propagates, and confirm timing with the DNS propagation checker.
  5. Renew a certificate to confirm nothing broke, then narrow the list over time.

The whole change is one record and, done in that order, carries no risk of locking yourself out.