You can write the best cold email of your life and have it land in spam because one of three small DNS records is missing or wrong. SPF, DKIM, and DMARC are how mail servers decide if you're really you โ and Gmail and Yahoo have hardened their requirements since February 2024. This is the plain-English walk-through.
The 30-second version
Three DNS records, one per protocol, configured on the domain you send from:
- SPF โ a list of IPs/hosts allowed to send on your behalf.
- DKIM โ a public key receivers use to verify your messages were cryptographically signed by you.
- DMARC โ a policy that tells receivers what to do when SPF or DKIM fails (none / quarantine / reject), plus where to send aggregate reports.
If any of these is broken or missing, large receivers (Gmail, Yahoo, Microsoft) will quietly drop your sending reputation. After that, even perfectly authenticated emails land in junk for weeks.
Setting up SPF
SPF is a single TXT record at the root of your sending domain. It lists every host that's allowed to send mail as @yourdomain.com.
A typical record using Google Workspace plus an SMTP provider looks like this:
@ IN TXT "v=spf1 include:_spf.google.com include:spf.mailprovider.com -all"Three things to get right
1. Only one SPF record per domain. Receivers reject domains with multiple SPF records โ common when a new tool is bolted on. Merge into one with extra include:entries.
2. End with -all (hard fail) or ~all (soft fail). Never +all โ that explicitly authorises everyone to spoof your domain. -all is the modern recommendation once you've tested.
3. Stay under the 10 DNS-lookup limit. Eachinclude: resolves at receive-time and counts against a hard ceiling of 10. Past 10, SPF returns permerror and messages are treated as unauthenticated. Use mxtoolbox SPF check after every change.
include: exactly as their docs specify โ don't paste an IP unless they explicitly tell you to. IPs rotate; include: follows the changes.Setting up DKIM
DKIM signs every outgoing message with a private key your provider holds, and you publish the matching public key in DNS. Receivers fetch your public key, verify the signature, and confirm the message hasn't been tampered with in transit.
The record is a TXT at a provider-defined selector:
google._domainkey.yourdomain.com. IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCS..."You don't write this by hand โ your sending provider (Google Workspace, Microsoft 365, Postmark, SES, etc.) generates the key pair and gives you the exact value to publish. The selector (google in the example) varies by provider.
If you send from multiple providers
Each provider gets its own selector โ they don't conflict. Workspace can publish under google._domainkey while Postmark publishes under pm._domainkey. Each message is signed by the provider that sent it; receivers verify against the matching selector. Don't share or copy DKIM keys between providers.
Setting up DMARC
DMARC ties SPF and DKIM together. It tells receivers two things: which result counts (the alignment rule), and what to do on failure (the policy).
A modest starter record at _dmarc.yourdomain.com:
_dmarc IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@yourdomain.com; pct=100; adkim=s; aspf=s"What each tag does
p=none: just monitor โ don't block on failure yet. Start here.p=quarantine: failed messages go to spam.p=reject: failed messages are bounced. The end state.rua: where aggregate XML reports are sent. Read them.adkim=s/aspf=s: strict alignment โ the authenticated domain must match the From: header exactly. User(relaxed) only if you send from subdomains.
The ramp
Don't go straight to p=reject. The right path is:
p=nonefor 2โ4 weeks. Read the aggregate reports. Fix the legitimate senders that aren't aligning.p=quarantine; pct=25for a week, then ramp to 100%.p=reject; pct=100.
DMARC reports are XML and tedious to read raw. Free aggregators (Postmark's DMARC Digests, dmarcian) turn them into a dashboard.
What Gmail and Yahoo now require on top
Since February 2024, bulk senders (anyone sending more than ~5K messages/day to Gmail/Yahoo addresses) must also:
- Ship a
List-Unsubscribeheader with both URL and mailto. - Ship
List-Unsubscribe-Post: List-Unsubscribe=One-Click(RFC 8058). - Keep their domain's spam complaint rate below 0.3% (and ideally 0.1%).
- Have a DMARC record at minimum
p=none.
We cover the requirements in more depth in Gmail and Yahoo's bulk sender requirements. The short version: every modern cold-email pipeline must honour opt-outs in seconds and prove its identity end-to-end, or the messages don't arrive.
A 10-minute verification routine
After every change, run through this checklist:
- Send a test message to a Gmail account you control.Open the message โ โฎ menu โ "Show original". You should see
SPF: PASS,DKIM: PASS,DMARC: PASS. - Run the same domain through mxtoolbox.com and check SPF, DKIM (with your selector), and DMARC pages.
- Check the "Authentication-Results" header in a received message:
All three should sayAuthentication-Results: mx.google.com; spf=pass smtp.mailfrom=yourdomain.com; dkim=pass header.i=@yourdomain.com header.s=google; dmarc=pass (p=quarantine) header.from=yourdomain.compass.
What ProspectFlow handles for you
The SPF / DKIM / DMARC records always live on your domain โ there's no way for any sending platform to handle that part for you (and you wouldn't want them to: that's your reputation, not theirs).
What we handle is everything above those records: pacing sends so volume doesn't spike, rotating SMTP accounts on paid plans, injecting the RFC 8058 one-click unsubscribe headers Gmail/Yahoo require, decoding and respecting bounces so dead addresses stop being retried, and surfacing reply detection so a recipient who responds is automatically dropped from follow-ups.