How-to guide · Harden and secure
How to set CAA records for your domain
In one sentence
You will have DNS CAA records that let only the CAs you really use issue certificates for your names, with separate wildcard rules and an address for violation reports.
- Platforms
- Linux
- macOS
- Windows
- Level
- Practitioner · 5 steps
On this page
Before you start
- Access to your domain’s DNS, through the provider’s web console, API or zone file.
- A list of the CAs your organisation uses. Step 1 shows how to build it.
dig(Linux, macOS or WSL) orcurlfor testing.
Why this matters: a CAA record tells every public CA which CAs may issue certificates for your names. CAA is defined in RFC 8659, and since 8 September 2017, CAs must check CAA records before issuing. One wrong record also blocks your own renewals, so build the list carefully.
Steps
1. Find every CA that issues for you
Do not guess. Look at what was really issued:
- Certificate Transparency. Search the logs for your domain and note every issuer of the last year, as in How to monitor Certificate Transparency.
- Your CDN, cloud and SaaS providers. Many obtain certificates for your names on your behalf, sometimes from more than one CA. Their documentation lists the CAA values they need.
- Your teams. Ask who buys certificates, and from whom.
Then look up each CA’s issuer domain name, the value CAA uses. The root programs’ shared database, CCADB, publishes the list for every CA. Some common ones, as listed there in October 2026:
| CA | CAA value |
|---|---|
| Let’s Encrypt | letsencrypt.org |
| Google Trust Services | pki.goog |
| DigiCert | digicert.com (also accepts www.digicert.com) |
| Sectigo | sectigo.com (also comodoca.com) |
| Amazon Trust Services | amazon.com (also amazontrust.com, awstrust.com, amazonaws.com) |
| GlobalSign | globalsign.com |
| SSL.com | ssl.com |
2. Check what is published today
Look at the records at your domain:
dig CAA example.com +short
On Windows, Resolve-DnsName does not know the CAA type. Use dig in WSL, or ask a DNS-over-HTTPS resolver:
curl -s -H "accept: application/dns-json" "https://cloudflare-dns.com/dns-query?name=debian.org&type=CAA"
{"Status":0,"TC":false,"RD":true,"RA":true,"AD":true,"CD":false,"Question":[{"name":"debian.org","type":257}],"Answer":[{"name":"debian.org","type":257,"TTL":28800,"data":"0 iodef \"mailto:dsa@debian.org\""},{"name":"debian.org","type":257,"TTL":28800,"data":"128 issue \"letsencrypt.org;validationmethods=dns-01;accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/346607\""},{"name":"debian.org","type":257,"TTL":28800,"data":"128 issuewild \";\""}]}
A CA does not stop at the exact name. It climbs the DNS tree and uses the first name that has CAA records (RFC 8659 section 3), and each lookup follows CNAMEs. This small script does the same, so you can see which records apply to any name:
#!/bin/sh
# caa-climb.sh: show the CAA records a CA would apply to a name (RFC 8659 section 3)
name="$1"
while [ -n "$name" ]; do
records=$(dig CAA "$name" +short | grep -E '^[0-9]+ ')
if [ -n "$records" ]; then
echo "CAA records that apply, found at $name:"
echo "$records"
exit 0
fi
case "$name" in
*.*) name="${name#*.}" ;;
*) name="" ;;
esac
done
echo "No CAA records: CAA does not restrict issuance."
Real results from 20 October 2026:
$ sh caa-climb.sh www.debian.org
CAA records that apply, found at debian.org:
128 issue "letsencrypt.org;validationmethods=dns-01;accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/346607"
0 iodef "mailto:dsa@debian.org"
128 issuewild ";"
$ sh caa-climb.sh example.com
No CAA records: CAA does not restrict issuance.
www.debian.org has no records of its own, so its parent’s apply. www.github.com is a CNAME to github.com, so a lookup at www.github.com already returns github.com’s records.
3. Write your records
A CAA record has three parts: flags (almost always 0), a tag and a value. The main tags:
issue: a CA that may issue certificates for the name. Severalissuerecords add up: any one of them allows that CA.issuewild: the same, for wildcard certificates only. If anyissuewildrecord exists, it replaces allissuerecords for wildcards (section 4.3).iodef: where a CA may report a request it refused.
One CA, no wildcards:
example.com. 3600 IN CAA 0 issue "letsencrypt.org"
example.com. 3600 IN CAA 0 issuewild ";"
example.com. 3600 IN CAA 0 iodef "mailto:security@example.com"
Two CAs, wildcards from only one:
example.com. 3600 IN CAA 0 issue "letsencrypt.org"
example.com. 3600 IN CAA 0 issue "digicert.com"
example.com. 3600 IN CAA 0 issuewild "digicert.com"
A domain that never needs certificates, such as a parked or mail-only domain:
parked-example.com. 3600 IN CAA 0 issue ";"
A subdomain with different rules. Records at a subdomain replace the parent’s for that name and everything below it; they are not merged. If shop.example.com uses a provider whose CA you do not use elsewhere, give it its own complete set:
shop.example.com. 3600 IN CAA 0 issue "pki.goog"
If the subdomain is a CNAME to the provider, you cannot add records next to it. The CA then uses the CNAME target’s CAA records if it has any, and otherwise yours from the parent domain, so your apex list must include the provider’s CA.
In a web console, enter the same three parts in separate fields. With the Azure CLI, for example:
az network dns record-set caa add-record -g my-dns-rg -z example.com -n @ \
--flags 0 --tag issue --value letsencrypt.org
4. Optional: lock issuance to your ACME account
RFC 8657 adds two parameters to issue and issuewild. accounturi allows only one ACME account, and validationmethods allows only certain challenge types:
example.com. 3600 IN CAA 0 issue "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1234567890; validationmethods=dns-01"
An attacker who briefly controls your web server can then no longer pass an HTTP challenge with an account of their own. Let’s Encrypt honours both parameters (Let’s Encrypt: CAA). For all public CAs, the rule is: public CAs must process the accounturi and validationmethods CAA parameters of RFC 8657 (from 15 March 2027). Until then, check that your CA supports them, because RFC 8657 section 5.2 warns that they are not effective unless the CA’s support is established beforehand.
Find your account URL in your ACME client’s account files. Keep a second record for a backup account, or every renewal stops when you replace the account.
5. Publish and test before the next renewal
- Lower the TTL to a few minutes while you change records, so mistakes are quick to fix.
- Publish the records, then run
caa-climb.shfor every important name, including names behind CNAMEs. - Request a test certificate from each CA you allowed, for example with
certbot certonly --dry-runagainst Let’s Encrypt staging, which checks CAA like production. - Raise the TTL again.
Check it worked
Run the climb for the names you care about and compare the result with your list from step 1:
for n in example.com www.example.com shop.example.com; do sh caa-climb.sh "$n"; echo; done
Then try what should fail. If you set issuewild ";", a dry run for *.example.com must be refused with a CAA error. A refusal you expected is proof that the CA reads your records.
If something goes wrong
- A renewal fails with
CAA record for example.com prevents issuance(Let’s Encrypt’s wording; other CAs word it differently). The CA is missing fromissue, or fromissuewildfor a wildcard. Add it, or remove the name from the certificate. - A provider’s renewal for one subdomain fails. That subdomain is a CNAME to the provider, or inherits your apex records, and your list lacks the provider’s CA. Check it with
caa-climb.sh. - The CA reports a lookup error or SERVFAIL. Some old DNS servers answer badly for unknown record types, and broken DNSSEC makes the answer bogus. Since 15 March 2026, public CAs must validate DNSSEC on every DNS query of their CAA lookups, so a bogus answer stops issuance. Test with
dig CAA example.com +dnssecthrough a validating resolver: you wantstatus: NOERROR, andSERVFAILpoints at broken DNSSEC. - A typo in the value, such as
letsencrypt.comor extra quotes typed into a console field, silently allows nobody. Compare each value with CCADB. - A critical flag (
128) on a tag the CA does not know stops that CA from issuing. Use flag0unless you mean it.
Why this works
Every public CA must look up CAA for every name in a certificate before it issues, and refuse if the records do not allow it (domain validation). CAA is checked only at issuance, by CAs; browsers never look at it, so it cannot block a certificate that already exists. That is why it pairs with Certificate Transparency, which shows you every certificate that does get issued.
Go deeper
- RFC 8659: CAA, including the tree climb and
issuewild. - RFC 8657: the
accounturiandvalidationmethodsparameters. - CA/Browser Forum Baseline Requirements, section 4.2.2.1: how public CAs must process CAA, including lookup failures.
- Let’s Encrypt: CAA: its CAA value, account URLs and validation-method names.
- CCADB: CAA identifiers: the values each CA recognises.
Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.