<!-- https://pkizerotohero.com/guides/caa-records/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> Set DNS CAA records: find the CAs you use, write issue, issuewild and iodef records, lock issuance to an ACME account, and test before renewals fail.

Source: https://pkizerotohero.com/guides/caa-records/ · Last reviewed: 2026-10-20

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.

Task

[Harden and secure](https://pkizerotohero.com/guides/#task-secure)

Platforms

- Linux
- macOS
- Windows

Who it is for

- [IT ops](https://pkizerotohero.com/start/#itops)
- [Expert](https://pkizerotohero.com/start/#expert)
- [Developer](https://pkizerotohero.com/start/#developer)

Level

Practitioner · 5 steps

## 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) or `curl` for testing.

Why this matters: a [CAA record](https://pkizerotohero.com/handbook/glossary/#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](https://pkizerotohero.com/guides/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](https://ccadb.my.salesforce-sites.com/ccadb/AllCAAIdentifiersReport). 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:

```bash
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:

```bash
curl -s -H "accept: application/dns-json" "https://cloudflare-dns.com/dns-query?name=debian.org&type=CAA"
```

```text
{"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](https://www.rfc-editor.org/rfc/rfc8659#section-3)), and each lookup follows CNAMEs. This small script does the same, so you can see which records apply to any name:

```bash
#!/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:

```text
$ 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. Several `issue` records add up: any one of them allows that CA.
- **`issuewild`**: the same, for wildcard certificates only. If any `issuewild` record exists, it replaces all `issue` records for wildcards ([section 4.3](https://www.rfc-editor.org/rfc/rfc8659#section-4.3)).
- **`iodef`**: where a CA may report a request it refused.

**One CA, no wildcards:**

```text
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:**

```text
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:

```text
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:

```text
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:

```bash
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](https://www.rfc-editor.org/rfc/rfc8657) adds two parameters to `issue` and `issuewild`. `accounturi` allows only one ACME account, and `validationmethods` allows only certain challenge types:

```text
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](https://letsencrypt.org/docs/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

1. Lower the TTL to a few minutes while you change records, so mistakes are quick to fix.
2. Publish the records, then run `caa-climb.sh` for every important name, including names behind CNAMEs.
3. Request a test certificate from each CA you allowed, for example with `certbot certonly --dry-run` against Let’s Encrypt staging, which checks CAA like production.
4. 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:

```bash
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 from `issue`, or from `issuewild` for 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 +dnssec` through a validating resolver: you want `status: NOERROR`, and `SERVFAIL` points at broken DNSSEC.
- **A typo in the value**, such as `letsencrypt.com` or 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 flag `0` unless 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](https://pkizerotohero.com/handbook/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](https://pkizerotohero.com/handbook/certificate-transparency/), which shows you every certificate that does get issued.

## Go deeper

- [RFC 8659](https://www.rfc-editor.org/rfc/rfc8659): CAA, including the tree climb and `issuewild`.
- [RFC 8657](https://www.rfc-editor.org/rfc/rfc8657): the `accounturi` and `validationmethods` parameters.
- [CA/Browser Forum Baseline Requirements](https://cabforum.org/working-groups/server/baseline-requirements/requirements/), section 4.2.2.1: how public CAs must process CAA, including lookup failures.
- [Let’s Encrypt: CAA](https://letsencrypt.org/docs/caa/): its CAA value, account URLs and validation-method names.
- [CCADB: CAA identifiers](https://ccadb.my.salesforce-sites.com/ccadb/AllCAAIdentifiersReport): the values each CA recognises.

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/guides/caa-records/).

Last reviewed 20 October 2026Edition 0.9

## Keep going

### Learn why it works

- [Chapter 11: Proving you own the name: DV, OV, EV and CAA](https://pkizerotohero.com/handbook/domain-validation/)
- [Chapter 17: Certificate Transparency](https://pkizerotohero.com/handbook/certificate-transparency/)

### Topics

- [Certificates](https://pkizerotohero.com/topics/certificates/)
- [Revocation and transparency](https://pkizerotohero.com/topics/revocation-and-transparency/)

## More guides: Harden and secure

- [How to configure TLS securely (versions, ciphers, HSTS)](https://pkizerotohero.com/guides/tls-configuration/)
- [How to set up mutual TLS (nginx and curl)](https://pkizerotohero.com/guides/mutual-tls-setup/)
