Server

SPF, DKIM, and DMARC End to End

By Prabath Thalangama· October 24, 2026· 4 min read
#email#spf#dkim#dmarc#security

Introduction

SPF, DKIM, and DMARC together answer one question for a receiving mail server: "is this message really from the domain in the From: header, and what should I do if it isn't?" Getting them wrong bounces your own newsletters; getting them right stops most spoofing. Here's how they fit and a rollout that's safe.

The three records

SPF — which servers may send

A TXT record at the domain apex listing authorised sending IPs:

example.com  TXT  "v=spf1 include:_spf.google.com ip4:203.0.113.10 ~all"

The receiver checks the envelope sender (MAIL FROM, aka Return-Path) domain's SPF against the connecting IP. ~all = soft-fail (accept but mark), -all = hard-fail. SPF breaks on forwarding — the forwarder's IP isn't in your record — which is why DMARC also considers DKIM.

Limit: max 10 DNS lookups total (each include, a, mx counts). Exceed it and SPF returns permerror. Use the SPF generator to count them.

DKIM — a cryptographic signature

Your outbound server signs selected headers + body with a private key. The public key is in DNS:

sel1._domainkey.example.com  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBg..."

The receiver fetches that key, verifies the signature. DKIM survives forwarding (the signature travels with the message) as long as the body and signed headers aren't modified. Generate the keypair with the DKIM generator.

DMARC — the policy and the reports

A TXT record at _dmarc.example.com:

_dmarc.example.com  TXT  "v=DMARC1; p=none; rua=mailto:[email protected]; adkim=r; aspf=r"

DMARC passes if SPF or DKIM passes AND is aligned. Alignment means the domain that passed matches the From: header domain:

  • SPF alignment — the Return-Path domain matches From: (relaxed = same organisational domain; strict = exact).
  • DKIM alignment — the d= in the signature matches From:.

A message can pass raw SPF/DKIM for some other domain and still fail DMARC because nothing aligned with From:.

p=none monitors only. p=quarantine → spam folder on failure. p=reject → bounce. pct= ramps it (pct=25 applies the policy to a quarter of failing mail). Build the record with the DMARC generator.

A safe rollout

  1. Publish SPF with ~all (not -all yet). Include every real sender: your mail server, Google/M365, your ESP (SendGrid, Mailgun), your ticketing system, monitoring alerts. Verify with the SPF checker.
  2. Enable DKIM signing on every outbound path. Each ESP has its own selector and key — add all of them. Verify with the DKIM checker.
  3. Publish DMARC p=none with rua=. Wait 1–2 weeks and read the aggregate reports (a parser or a service like Postmark's free DMARC tool). They tell you every source sending as your domain and whether it aligns.
  4. Fix the gaps the reports reveal — a forgotten SaaS tool, a subdomain, a marketing platform not signing.
  5. Move to p=quarantine; pct=25, then pct=100, watching reports at each step.
  6. Move to p=reject. Now tighten SPF to -all.
  7. Consider BIMI once you're at p=quarantine+ — verify with the BIMI checker.

Never jump straight to p=reject. You will have a sender you forgot about.

Verification and troubleshooting

dig +short TXT example.com | grep spf1
dig +short TXT sel1._domainkey.example.com
dig +short TXT _dmarc.example.com

# Send a test to a checker address and read the auth results, or:
# (in a received message header)
#   Authentication-Results: mx.google.com;
#     spf=pass ... dkim=pass header.d=example.com dmarc=pass
  • DMARC fails but SPF and DKIM both "pass" — alignment. The passing domain isn't your From: domain. Common with ESPs that use their own Return-Path; fix by setting a custom Return-Path/CNAME on your domain, or rely on DKIM alignment (d=example.com).
  • SPF permerror — over 10 lookups. Flatten includes to IPs or drop unused senders.
  • DKIM fail intermittently — a mailing list or forwarder rewrites the Subject or footer, breaking the body hash. This is expected; DMARC still passes if SPF aligns, or the list should use ARC.
  • Your newsletter goes to spam after p=reject — the ESP isn't aligned. Set up the ESP's domain-authentication (custom CNAMEs) so DKIM d= matches.
  • No aggregate reports arriving — rua= address wrong, or the mailbox domain's DMARC blocks the reports. Use a dedicated address.
  • Subdomain mail rejected — DMARC's sp= (subdomain policy) inherits p= unless set. Add sp=none while you sort subdomains out.
PrabathStuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.