DKIM, SPF, and DMARC Explained Simply
A developer-friendly explanation of email authentication. What these acronyms mean, why they matter, and how to set them up correctly.
Your password reset emails are landing in spam. Users can't log in. Support tickets pile up. You check your code—it's fine. The email was sent. So where did it go?
The culprit: three missing DNS records.
| Protocol | What it does | Analogy |
|---|---|---|
| SPF | Lists which servers can send email for your domain | Guest list |
| DKIM | Adds a digital signature proving the email wasn't tampered with | Wax seal |
| DMARC | Tells receivers what to do when SPF/DKIM fail | Rule book |
All three work together. Missing any one weakens the whole system.
The Problem These Solve
Email was invented in 1971. Security wasn't a priority. Anyone can send an email claiming to be from ceo@yourcompany.com. That's how phishing works.
SPF, DKIM, and DMARC are band-aids on this fundamental design flaw. They let receiving servers verify that you're actually authorized to send email from your domain.
Without authentication, your emails look like this to Gmail:
Authentication-Results: mx.google.com; spf=none (no SPF record found) dkim=none (no signature) dmarc=fail
With proper setup:
Authentication-Results: mx.google.com; spf=pass (domain of hello@yourdomain.com designates 123.45.67.89 as permitted sender) dkim=pass header.d=yourdomain.com header.s=sendpigeon dmarc=pass (p=QUARANTINE)
The difference between inbox and spam.
SPF: Who Can Send
What it does: Lists which servers are allowed to send email for your domain.
Analogy: It's like a guest list. "These IP addresses/services are authorized to speak on my behalf."
How it works
- You publish a TXT record in DNS
- Receiving server gets an email "from" your domain
- Server checks: "Is the sending IP on the SPF list?"
- If yes, pass. If no, fail.
Example record
v=spf1 include:sendpigeon.com include:_spf.google.com ~all
| Part | Meaning |
|---|---|
v=spf1 | Version 1 of SPF |
include:sendpigeon.com | Trust servers listed in SendPigeon's SPF |
include:_spf.google.com | Also trust Google (if you use Gmail) |
~all | Soft fail everything else (treat with suspicion but don't reject) |
Common mistakes
- Too many lookups: SPF allows max 10 DNS lookups. Each
includecounts. - Using
+all: This authorizes everyone. Never do this. - Forgetting a service: If you use Mailchimp AND SendPigeon, include both.
DKIM: Message Integrity
What it does: Adds a digital signature to prove the email wasn't modified in transit.
Analogy: It's like a wax seal on a letter. If the seal is broken, you know someone tampered with it.
How it works
- You generate a public/private key pair
- Public key goes in DNS as a TXT record
- When you send an email, your server signs it with the private key
- Receiving server uses the public key to verify the signature
Example DNS record
sendpigeon._domainkey.yourdomain.com TXT "v=DKIM1; k=rsa; p=MIGfMA0GCS..."
The record name format is selector._domainkey.yourdomain.com. The selector (like sendpigeon) identifies which key to use—you can have multiple.
What gets signed
DKIM signs specific headers and the body:
- From address
- Subject
- Date
- Body content hash
If any of these change after signing, verification fails.
What a DKIM header looks like
When you inspect an email's raw headers, you'll see something like:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=yourdomain.com; s=sendpigeon;
h=from:to:subject:date:message-id;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
b=AuUoFEfDxTDkHlLXSZEpZj79LICEps6eda7W3deTVFOk4yAUoqOB...
Common mistakes
- Key too short: Use at least 1024-bit keys. 2048-bit is better.
- Forgetting to rotate: Keys should be rotated periodically.
- Wrong selector: Make sure you're using the selector your email provider gave you.
DMARC: The Policy Layer
What it does: Tells receiving servers what to do when SPF/DKIM fail, and where to send reports.
Analogy: It's the rule book. "If authentication fails, here's what you should do about it."
How it works
- Receiving server checks SPF and DKIM
- Receiving server looks up your DMARC policy
- If SPF or DKIM fails, server follows your policy
- Server sends you a report about what happened
Example record
_dmarc.yourdomain.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com"
| Part | Meaning |
|---|---|
v=DMARC1 | Version 1 of DMARC |
p=quarantine | Put failing emails in spam |
rua=mailto:... | Send aggregate reports to this address |
Policy progression
Start permissive and tighten gradually:
| Policy | What happens | When to use |
|---|---|---|
p=none | Monitor only. No action taken. | Start here. See what's happening. |
p=quarantine | Failures go to spam. | After 2-4 weeks of monitoring. |
p=reject | Failures blocked entirely. | When you're confident everything's set up. |
Don't jump to p=reject immediately. You'll block legitimate email you forgot about—like that newsletter service your marketing team set up last year.
DMARC alignment
Here's the tricky part. DMARC requires "alignment"—the domain in the From header must match:
- The domain that passed SPF, OR
- The domain that signed with DKIM
If your From address is hello@myapp.com, but you send through a service that uses bounce.emailservice.com for SPF... that's a mismatch. You need DKIM alignment instead.
How They Work Together
Email arrives from "you@yourdomain.com"
│
├─► SPF Check: Is sending IP authorized?
│ └─► Pass/Fail
│
├─► DKIM Check: Is signature valid?
│ └─► Pass/Fail
│
└─► DMARC Check: Did either SPF or DKIM pass WITH alignment?
│
├─► Yes → Deliver normally
└─► No → Apply DMARC policy (none/quarantine/reject)
Quick Setup
Set up SPF
Add a TXT record to your domain's DNS:
v=spf1 include:YOUR_EMAIL_PROVIDER ~all
Replace YOUR_EMAIL_PROVIDER with your actual provider's include (e.g., sendpigeon.com).
Set up DKIM
Get the DKIM record from your email provider's dashboard. It'll look something like:
sendpigeon._domainkey.yourdomain.com TXT "v=DKIM1; k=rsa; p=MIGf..."
Add it to your DNS, then enable DKIM signing in your provider's settings.
Set up DMARC
Add a TXT record at _dmarc.yourdomain.com:
v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com
Start with p=none to monitor without affecting delivery.
Verify everything
Send a test email to yourself. View the original/raw message and look for:
- spf=pass
- dkim=pass
- dmarc=pass
In Gmail: Open email → Three dots → "Show original"
Tighten DMARC policy
After a few weeks of monitoring reports, upgrade from p=none → p=quarantine → p=reject.
Tools for Testing
- SendPigeon Deliverability Checker - Free instant check of SPF, DKIM, DMARC with score and recommendations
- MXToolbox - Check SPF, DKIM, DMARC records
- Mail-tester - Send a test email, get a deliverability score
- Google Postmaster - See how Gmail views your domain
Bottom Line
- SPF = Who can send
- DKIM = Message wasn't tampered
- DMARC = What to do when checks fail
Set them up, monitor the reports, and gradually enforce stricter policies. Your emails will thank you.
Next Steps
- Check our email deliverability checklist for everything else that affects inbox placement
- See our framework guides for sending emails from Next.js, Remix, and more
- Browse our email templates for ready-to-use HTML