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

RecordLives atAnswers
SPFthe domain apex, TXTIs this IP allowed to send for this domain?
DKIM<selector>._domainkey.<domain>, TXTWas this message signed by a key the domain publishes?
DMARC_dmarc.<domain>, TXTWhat 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
MechanismMatchesExample
ip4:An IPv4 address or CIDRip4:198.51.100.0/24
ip6:An IPv6 address or prefixip6:2001:db8::/32
aThe domain's own A/AAAA recordsa or a:mail.example.com
mxThe IPs of the domain's MX hostsmx or mx:example.com
include:Recursively evaluate another domain's SPFinclude:_spf.google.com
exists:A macro-expanded name resolvesexists:%{i}._spf.example.com
redirect=Replace this policy entirelyredirect=_spf.example.net
ptrReverse DNS matches. Deprecated, never useptr
QualifierPrefixResultMeaning
PassnonepassAuthorized
Fail-failNot authorized, reject
SoftFail~softfailProbably not authorized, accept and mark
Neutral?neutralNo 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.

TagMeaning
v=DKIM1Version, required and first
k=rsa | k=ed25519Key type, rsa in practice
p=Base64 public key. An empty p= revokes the selector
h=sha256Permitted hash algorithms
s=emailService type, email or *
t=yTesting mode, receivers ignore failures
t=sNo 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"
TagDefaultMeaning
v=DMARC1requiredVersion, must be first
p=requiredPolicy: none, quarantine, reject
sp=inherits pPolicy for subdomains
rua=—Aggregate report address, mailto:
ruf=—Forensic report address, rarely honored now
pct=100Percentage of failing mail the policy applies to
adkim=rDKIM alignment: r relaxed, s strict
aspf=rSPF alignment: r relaxed, s strict
fo=0When to send forensic reports: 0, 1, d, s
ri=86400Aggregate report interval, seconds
p=Receiver doesUse it when
noneNothing, but sends reportsWeek 1. Monitoring only
quarantineSpam folderYou believe you found every sender
rejectBounce at SMTP timeYou 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.