DNS Cheatsheet
Email Records (SPF DKIM DMARC)
Use this DNS reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
The three records and what each proves
| Record | Lives at | Answers |
|---|---|---|
| SPF | the domain apex, TXT | Is this IP allowed to send for this domain? |
| DKIM | <selector>._domainkey.<domain>, TXT | Was this message signed by a key the domain publishes? |
| DMARC | _dmarc.<domain>, TXT | What should a receiver do when SPF and DKIM disagree with the From header? |
All three are TXT records. DMARC is the only one that sets policy, and it
only takes effect because it requires alignment: the domain that passed SPF or
DKIM must match the domain in the visible From: header.
dig example.com TXT +short | grep spf1 dig sel1._domainkey.example.com TXT +short dig _dmarc.example.com TXT +short
SPF
One TXT record at the apex, starting v=spf1, ending in an all-qualifier.
example.com. 3600 IN TXT "v=spf1 mx include:_spf.google.com ~all"
google.com. TXT "v=spf1 include:_spf.google.com ~all" ; live right now| Mechanism | Matches | Example |
|---|---|---|
ip4: | An IPv4 address or CIDR | ip4:198.51.100.0/24 |
ip6: | An IPv6 address or prefix | ip6:2001:db8::/32 |
a | The domain's own A/AAAA records | a or a:mail.example.com |
mx | The IPs of the domain's MX hosts | mx or mx:example.com |
include: | Recursively evaluate another domain's SPF | include:_spf.google.com |
exists: | A macro-expanded name resolves | exists:%{i}._spf.example.com |
redirect= | Replace this policy entirely | redirect=_spf.example.net |
ptr | Reverse DNS matches. Deprecated, never use | ptr |
| Qualifier | Prefix | Result | Meaning |
|---|---|---|---|
| Pass | none | pass | Authorized |
| Fail | - | fail | Not authorized, reject |
| SoftFail | ~ | softfail | Probably not authorized, accept and mark |
| Neutral | ? | neutral | No assertion |
-all is hard fail, ~all is soft fail. Start with ~all while you find
every sender, then move to -all. Anything other than -all or ~all at the
end means SPF is doing almost nothing.
The 10 DNS lookup limit is the trap. Every include, a, mx, exists,
and redirect costs a lookup, recursively. Exceed 10 and the result is
permerror, which most receivers treat as a fail.
dig example.com TXT +short | grep spf1 # your record dig _spf.google.com TXT +short # then each include, recursively
- ip4: and ip6: cost zero lookups. Flatten includes into IPs when you are
near the limit.
- One SPF record per domain, exactly. Two v=spf1 TXT records is permerror.
- SPF checks the envelope sender (MAIL FROM), not the From: header, which
is why SPF alone does not stop spoofing.
- SPF breaks on forwarding. The forwarder's IP is not in your record. This is what
DKIM and DMARC alignment exist to solve.
DKIM
A public key published at a selector, and a signature added to the message headers by the sending server.
sel1._domainkey.example.com. 3600 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1x8kQ2..."
Long RSA keys exceed the 255-byte TXT string limit, so they are split into several quoted strings inside one record, concatenated with no separator.
| Tag | Meaning |
|---|---|
v=DKIM1 | Version, required and first |
k=rsa | k=ed25519 | Key type, rsa in practice |
p= | Base64 public key. An empty p= revokes the selector |
h=sha256 | Permitted hash algorithms |
s=email | Service type, email or * |
t=y | Testing mode, receivers ignore failures |
t=s | No subdomain use of this key |
The corresponding header on a signed message:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=sel1; t=1789459200; bh=<body hash>; h=from:to:subject:date; b=<signature>
d= is the signing domain and s= is the selector, so together they tell you
exactly which TXT record to fetch:
dig 20230601._domainkey.google.com TXT +short- Key rotation is why selectors exist. Publish a new selector, switch the
signer to it, wait out in-flight mail, then blank the old p=.
- 2048-bit RSA is the current norm. 1024-bit still validates but is weak, and
some receivers now ignore it.
- DKIM survives forwarding as long as the forwarder does not modify the signed
headers or the body. Mailing lists that append footers break it.
DMARC
One TXT record at _dmarc.<domain>. It tells receivers what to do and where to
send reports.
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com; pct=100; adkim=s; aspf=s"
_dmarc.google.com. TXT "v=DMARC1; p=reject; rua=mailto:mailauth-reports@google.com"| Tag | Default | Meaning |
|---|---|---|
v=DMARC1 | required | Version, must be first |
p= | required | Policy: none, quarantine, reject |
sp= | inherits p | Policy for subdomains |
rua= | — | Aggregate report address, mailto: |
ruf= | — | Forensic report address, rarely honored now |
pct= | 100 | Percentage of failing mail the policy applies to |
adkim= | r | DKIM alignment: r relaxed, s strict |
aspf= | r | SPF alignment: r relaxed, s strict |
fo= | 0 | When to send forensic reports: 0, 1, d, s |
ri= | 86400 | Aggregate report interval, seconds |
p= | Receiver does | Use it when |
|---|---|---|
none | Nothing, but sends reports | Week 1. Monitoring only |
quarantine | Spam folder | You believe you found every sender |
reject | Bounce at SMTP time | You are certain. The end state |
Alignment is the whole point. DMARC passes when SPF or DKIM passes and
that domain aligns with the From: header domain.
- Relaxed (r): organizational domains must match. mail.example.com aligns
with example.com.
- Strict (s): the domains must be identical.
The rollout, and skipping a step is how you lose mail:
1. p=none; rua=mailto:dmarc@example.com and leave it for two to four weeks.
2. Read the aggregate reports. Find every legitimate sender, including the
invoicing SaaS nobody remembers.
3. Fix each one: add to SPF, or set up DKIM signing with your domain.
4. p=quarantine; pct=10, then raise pct gradually to 100.
5. p=reject.
dig _dmarc.sub.example.com TXT +short # empty means it inherits via sp= or p=Gotchas
- Two v=spf1 records is permerror, and the failure is silent until mail
starts bouncing. There is exactly one SPF record per name.
- The SPF 10-lookup limit counts recursively. Three includes that each
include three more is already over.
- p=reject with an incomplete SPF loses real mail. Every rollout that went
badly skipped the p=none monitoring phase.
- SPF alone does not prevent spoofing. It checks the envelope sender, which
users never see. Only DMARC alignment ties authentication to the visible
From:.
- An empty DKIM p= revokes the key. That is the correct way to retire a
selector, and an accidental empty p= fails every signature.
- DMARC applies to the From: header's organizational domain, so a
subdomain with no record inherits the parent's p= unless sp= overrides it.
- _dmarc is a subdomain, not a prefix on the TXT value. Providers whose UI
asks for "name" want _dmarc, and pasting _dmarc.example.com produces
_dmarc.example.com.example.com.
- DKIM t=y means testing and tells receivers to ignore failures. Leaving it in
production makes DKIM decorative.