Skip to content
ecdsa.com

DKIM vs SPF vs DMARC: what each one checks and why you need all three

SPF vouches for the sending server, DKIM signs the message, DMARC connects both to the From address people see and tells receivers what to do when they fail.

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

SPFDKIMDMARC
SpecificationRFC 7208 (2014)RFC 6376 (2011), RFC 8301, RFC 8463RFC 9989, 9990, 9991 (May 2026), replacing RFC 7489
Question answeredMay 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 checkedMAIL FROM (Return-Path) and HELOThe d= domain in DKIM-SignatureThe From: header domain
Published atTXT on the domainTXT at selector._domainkey.domainTXT at _dmarc.domain
Survives plain forwardingNo (the forwarder's IP is not listed)Yes, if the message is not modifiedYes, through DKIM
Survives mailing lists that edit messagesNoNo (body or signed headers change)Only if the list rewrites From: or the receiver trusts ARC
Hard limits10 DNS-querying terms per evaluation, 2 void lookupsRSA at least 1024 bits (2048 recommended); rsa-sha1 forbiddenDNS tree walk of at most 8 queries
Effect on its ownResult only; receivers decideResult only; receivers decideDomain owner's policy: none, quarantine, reject
ReportingNoneNoneAggregate 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).

Read SPF, DMARC and DKIM records with dig
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
Output
== 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.

FlowSPFDKIMDMARC
Your own server, signing as example.compass, alignedpass, alignedpass
Email service provider with its own bounce domain, signing as example.compass for the provider's domain, not alignedpass, alignedpass (DKIM)
Provider that signs only with its own domainpass, not alignedpass, not alignedfail
Forwarded unchanged by a mailbox rulefail (forwarder's IP)pass, alignedpass (DKIM)
Mailing list adds a footer or subject tagfail or not alignedfail (body hash)fail, unless From: is rewritten or ARC is trusted
Spoofed From: from someone else's serverfail or nonenonefail, 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 -all on 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 (or sp=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

  1. Inventory senders using DMARC aggregate reports at p=none for two to four weeks; every legitimate source must show aligned DKIM, aligned SPF, or both.
  2. 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.
  3. Trim SPF below 10 lookups by removing unused includes; flatten into IP ranges only if you can keep them updated automatically.
  4. Enforce gradually: p=quarantine with t=y to observe, then p=quarantine, then p=reject; keep rua reports flowing.
  5. Protect subdomains with sp or np, and publish null records on every domain that should not send mail.

References

All comparisons