DKIM vs SPF vs DMARC is not a choice between alternatives: the three standards answer different questions and only work as a set. SPF lists which servers may send mail for the envelope domain; DKIM attaches a signature made with a key published under a domain; DMARC requires that at least one of them passes for a domain matching the visible From: address and publishes the domain owner's policy and reporting addresses.
At a glance
| SPF | DKIM | DMARC | |
|---|---|---|---|
| Specification | RFC 7208 (2014) | RFC 6376 (2011), RFC 8301, RFC 8463 | RFC 9989, 9990, 9991 (May 2026), replacing RFC 7489 |
| Question answered | May this IP send for the envelope domain? | Did this domain sign this content, unmodified? | Does an authenticated domain match the From: domain, and what if not? |
| Identity checked | MAIL FROM (Return-Path) and HELO | The d= domain in DKIM-Signature | The From: header domain |
| Published at | TXT on the domain | TXT at selector._domainkey.domain | TXT at _dmarc.domain |
| Survives plain forwarding | No (the forwarder's IP is not listed) | Yes, if the message is not modified | Yes, through DKIM |
| Survives mailing lists that edit messages | No | No (body or signed headers change) | Only if the list rewrites From: or the receiver trusts ARC |
| Hard limits | 10 DNS-querying terms per evaluation, 2 void lookups | RSA at least 1024 bits (2048 recommended); rsa-sha1 forbidden | DNS tree walk of at most 8 queries |
| Effect on its own | Result only; receivers decide | Result only; receivers decide | Domain owner's policy: none, quarantine, reject |
| Reporting | None | None | Aggregate XML reports (rua), failure reports (ruf) |
How each one works
SPF is evaluated during the SMTP session: the receiver takes the domain of the envelope sender (or of HELO for bounces), fetches its TXT record and checks the connecting IP against the listed ip4, ip6, a, mx and include terms. The result is pass, fail (-all), softfail (~all), neutral, none, or an error. Every include, a, mx, ptr, exists and redirect costs a DNS lookup, and more than 10 makes the result permerror (RFC 7208 §4.6.4), which breaks SPF for all mail of the domain.
DKIM hashes the canonicalized body (bh=) and the listed headers (h=) and signs them with the domain's private key. The receiver fetches the public key from a TXT record named after the s= selector and the d= domain (for example google._domainkey.github.com) and verifies the signature. RFC 8301 forbids rsa-sha1 and requires keys of at least 1024 bits; RFC 8463 adds ed25519-sha256, which is usually published alongside an RSA signature because verifier support is incomplete.
DMARC runs after both: it passes when SPF passes for a domain aligned with From:, or a DKIM signature verifies for a domain aligned with From:. Alignment is relaxed by default (same organizational domain, so mail.example.com aligns with example.com) or strict with aspf=s and adkim=s.
The script below reads real records (20 October 2026, dig 9.10.6 and OpenSSL 3.6.5).
echo "== SPF: google.com and its include"
dig +short TXT google.com | grep "v=spf1"
dig +short TXT _spf.google.com
echo "== DMARC: google.com and github.com"
dig +short TXT _dmarc.google.com
dig +short TXT _dmarc.github.com
echo "== DKIM: key size behind two github.com selectors"
for sel in google pf2023; do
printf '%s: ' "$sel"
dig +short TXT "$sel._domainkey.github.com" | tr -d '" \n' | sed 's/.*p=//' \
| openssl base64 -d -A | openssl pkey -pubin -inform DER -noout -text | head -1
done
== SPF: google.com and its include
"v=spf1 include:_spf.google.com ~all"
"v=spf1 ip4:74.125.0.0/16 ip4:209.85.128.0/17 ip6:2001:4860:4864::/56 ip6:2404:6800:4864::/56 ip6:2607:f8b0:4864::/56 ip6:2800:3f0:4864::/56 ip6:2a00:1450:4864::/56 ip6:2c0f:fb50:4864::/56 ~all"
== DMARC: google.com and github.com
"v=DMARC1; p=reject; rua=mailto:mailauth-reports@google.com"
"v=DMARC1; p=quarantine; sp=reject; pct=100; rua=mailto:dmarc@github.com; ruf=mailto:dmarc@github.com; fo=1"
== DKIM: key size behind two github.com selectors
google: Public-Key: (2048 bit)
pf2023: Public-Key: (1024 bit)
Three details are worth noticing. Google's SPF ends in ~all (softfail) while its DMARC policy is reject: with DMARC enforced, the DMARC result decides, so the SPF qualifier matters little. GitHub still publishes pct=100, a tag RFC 9989 removed; receivers ignore unknown tags and 100 was the default, so it is harmless. And one of GitHub's selectors still uses a 1024-bit key, the minimum RFC 8301 allows. The email authentication analyzer runs the same checks for any domain.
Alignment decides the outcome
The table shows typical flows for a message with From: news@example.com.
| Flow | SPF | DKIM | DMARC |
|---|---|---|---|
Your own server, signing as example.com | pass, aligned | pass, aligned | pass |
Email service provider with its own bounce domain, signing as example.com | pass for the provider's domain, not aligned | pass, aligned | pass (DKIM) |
| Provider that signs only with its own domain | pass, not aligned | pass, not aligned | fail |
| Forwarded unchanged by a mailbox rule | fail (forwarder's IP) | pass, aligned | pass (DKIM) |
| Mailing list adds a footer or subject tag | fail or not aligned | fail (body hash) | fail, unless From: is rewritten or ARC is trusted |
Spoofed From: from someone else's server | fail or none | none | fail, policy applied |
The third row is the most common deployment mistake: a sender passes SPF and DKIM, yet fails DMARC because neither authenticated domain is yours. Configure each provider to sign with a key under your domain (usually a CNAME to the provider's key), and use a custom bounce domain if you want SPF to align as well.
Forwarding, mailing lists and ARC
SPF authenticates the last hop, so any forwarding breaks it; Sender Rewriting Schemes fix SPF for the forwarder's own domain but never align it. DKIM authenticates content, so it survives relays that leave the message alone and fails when lists add footers or tags. ARC (RFC 8617, Experimental) lets an intermediary seal the authentication results it saw, and receivers that trust that sealer can accept the message despite the DMARC failure; the ARC viewer shows the chain. The remaining weakness is DKIM replay: a validly signed message can be re-sent to new recipients, which an IETF working group addresses in its DKIM2 drafts.
What mailbox providers require
Since 1 February 2024 Gmail requires every sender to use SPF or DKIM, valid forward and reverse DNS, TLS and a reported spam rate below 0.3%. Senders of 5,000 or more messages a day to personal Gmail accounts also need SPF and DKIM, a DMARC record (p=none is enough), From: aligned with SPF or DKIM, and one-click unsubscribe (RFC 8058) for marketing mail. Yahoo introduced matching rules at the same time. Microsoft applied the same thresholds to Outlook.com from 5 May 2025 and rejects non-compliant mail with 550 5.7.515. BIMI logos additionally require DMARC at quarantine or reject.
RFC 9989 (May 2026) moved DMARC to the Standards Track and changed the record: pct, rf and ri are gone, t=y replaces pct=0 for testing, np sets a policy for non-existent subdomains, psd marks public suffix domains, and a DNS tree walk replaces the Public Suffix List for finding the organizational domain.
What each one is for
- SPF: authorize your own outbound servers and providers that use your domain as bounce address; publish
v=spf1 -allon domains and subdomains that never send mail. - DKIM: sign all outgoing mail with your own domain, including mail sent through providers; rotate selectors at least yearly and replace 1024-bit keys with 2048-bit keys.
- DMARC: collect reports first, then enforce; add
np=reject(orsp=reject) so attackers cannot use invented subdomains. - Parked domains:
v=spf1 -all,v=DMARC1; p=reject;and no DKIM selectors. - Transport and content protection are different problems: use MTA-STS for TLS between servers and S/MIME or OpenPGP for end-to-end signing.
Migration notes
- Inventory senders using DMARC aggregate reports at
p=nonefor two to four weeks; every legitimate source must show aligned DKIM, aligned SPF, or both. - Fix DKIM first: enable custom-domain signing at each provider, publish 2048-bit keys (the DKIM record generator splits them into 255-character strings) and verify with the DKIM debugger.
- Trim SPF below 10 lookups by removing unused includes; flatten into IP ranges only if you can keep them updated automatically.
- Enforce gradually:
p=quarantinewitht=yto observe, thenp=quarantine, thenp=reject; keepruareports flowing. - Protect subdomains with
spornp, and publish null records on every domain that should not send mail.
References
- RFC 7208, SPF (§4.6.4 lookup limits)
- RFC 6376, DKIM, RFC 8301, DKIM crypto update, RFC 8463, Ed25519 for DKIM
- RFC 9989, DMARC, RFC 9990, aggregate reporting, RFC 9991, failure reporting
- RFC 8617, ARC
- RFC 8058, one-click unsubscribe
- Google: email sender guidelines
- Microsoft: strengthening email ecosystem requirements for high-volume senders