Fundamentals

How to Set Up SPF, DKIM and DMARC (and Fix Them When They Break)

A step-by-step guide to publishing SPF, DKIM and DMARC records, with the exact record values to paste, the DNS steps for Cloudflare and Route 53, and fixes for the SPF 10-lookup limit, a p=none policy, and duplicate records.

By Hussam Abd 11 min read

Key takeaways

SPF, DKIM and DMARC are three DNS records that together tell receiving mail servers whether a message claiming to come from your domain really did. Get them right and your mail is trusted. Get them wrong — or leave them half-configured, which is the more common state — and your legitimate mail gets filtered while anyone can still forge your domain.

This guide is the practical half. If you want the concepts first, read the deliverability guide. Here we publish each record, then work through the specific errors these three standards produce most often. You can check what your domain looks like right now with our free SPF, DKIM and DMARC checker — it reports each record, counts your SPF lookups, and links back to the section here that fixes what it finds.

Before you start: where DNS records live

All three are TXT records, added wherever your domain's DNS is hosted — usually your registrar, Cloudflare, or Route 53. Two details trip people up:

Publishing an SPF record

SPF is a single TXT record at the root of your domain listing every service allowed to send mail as you. Create a TXT record with the name @ (or your bare domain) and a value that starts with v=spf1, names each sender, and ends with an all mechanism:

v=spf1 include:_spf.google.com include:amazonses.com ~all

The include values come from each provider, and you need one for every service that sends on your behalf — your mailbox provider, your marketing platform, your transactional sender, your helpdesk, your invoicing tool. The most common values:

SenderInclude
Google Workspaceinclude:_spf.google.com
Microsoft 365include:spf.protection.outlook.com
Amazon SESinclude:amazonses.com
SendGridinclude:sendgrid.net
Mailguninclude:mailgun.org
Postmarkinclude:spf.mtasv.net

Forgetting a sender is the usual cause of "our invoices go to spam" — the invoicing tool was never in the record.

One record, not two

A domain may publish exactly one SPF record. Two is not additive: receivers see more than one v=spf1 TXT record, treat it as a permanent error, and SPF fails entirely — including for the senders that were listed correctly. This happens when a new tool's setup wizard adds its own record rather than editing yours.

The fix is to merge them. Take the mechanisms from both records, put them in one, and delete the other:

Before (broken):
  v=spf1 include:_spf.google.com ~all
  v=spf1 include:sendgrid.net ~all

After:
  v=spf1 include:_spf.google.com include:sendgrid.net ~all

Ending the record: ~all or -all

The final mechanism tells receivers what to do about servers that are not on your list, and the qualifier in front of all is what carries the meaning:

EndingMeaningWhen to use it
-allFail — reject unlisted serversOnce you are confident every sender is listed
~allSoftfail — accept but mark suspiciousWhile you are still rolling out
?allNeutral — no opinionEffectively the same as having no policy
+allPass — anyone may send as youNever

+all deserves a specific warning: it authorises the entire internet to send mail as your domain, which is worse than publishing no SPF record at all, because it actively vouches for forgeries. It usually appears by accident, copied from an example. If you find it on your domain, change it to ~all today.

Leaving the all mechanism off entirely has a similar effect to ?all: the check returns neutral, and you have told receivers nothing.

The 10-lookup limit, and how to get back under it

This is the error that catches out otherwise careful senders, because the record looks perfectly correct. SPF permits a maximum of 10 DNS-querying mechanisms per evaluation: include, a, mx, ptr, exists and redirect. Crucially they are counted recursively — every include pulls in the lookups inside the record it points at. Three includes can easily cost nine lookups.

Go over the limit and the result is permerror. SPF does not degrade gracefully; it stops passing, and if your DMARC policy relies on SPF alignment, DMARC starts failing with it. Nothing in your DNS looks wrong, which is why this one takes so long to diagnose. Our checker follows your includes and gives you the count.

Three ways down, in the order worth trying:

  1. Remove senders you no longer use. This alone usually solves it. Most over-limit records list a marketing platform from two years ago and a helpdesk that was replaced. Each dead include is a free lookup back.
  2. Drop includes for services that do not send as your domain. A tool that sends from its own domain with a reply-to of yours does not need to be in your SPF record at all.
  3. Flatten the heaviest include. Replace it with the ip4: and ip6: ranges it currently resolves to. This works — IP mechanisms cost no lookups — but it is a maintenance commitment: when the provider changes its IPs, your record is silently wrong. Only flatten what you must, and re-check it on a schedule, or use a service that maintains the flattening for you.

Setting up DKIM

DKIM signs each outgoing message with a private key held by your sending platform, and publishes the matching public key in your DNS. Receivers verify the signature and learn that the message genuinely came from your domain and was not altered in transit. Because the signature travels with the message, DKIM survives forwarding in cases where SPF does not.

You do not write a DKIM record by hand. Your sending platform generates the key pair and gives you a record to publish:

The record lives at {selector}._domainkey.yourdomain.com, where the selector is a label the platform chooses — google, selector1, s1, pm and so on. Each sending service publishes its own selector, so a domain sending through three platforms will have three DKIM records, and that is correct.

One consequence worth knowing: because selectors are arbitrary and DNS gives no way to list them, no tool can prove a domain has no DKIM key. Ours probes the selectors the major platforms use and tells you when it finds none, but a custom selector is invisible to it. If you know DKIM is signing, check the platform's own status screen rather than trusting any external checker on this one point.

Publishing a DMARC record

DMARC ties the other two together. It publishes a policy telling receivers what to do with mail that fails authentication, and it adds alignment: the requirement that the domain which passed SPF or DKIM matches the domain your recipient sees in the From field. Alignment is the part that actually stops spoofing, because a forger can pass SPF for their own domain while displaying yours.

Create a TXT record with the name _dmarc and this value:

v=DMARC1; p=none; rua=mailto:[email protected]

Start at p=none deliberately. It changes nothing about delivery, which means you cannot break your own mail while you find out what is actually sending as your domain.

The rua address is the whole point of starting at none

The rua tag is where receivers send daily aggregate reports: XML summaries of every source sending as your domain and whether it passed. Without it, p=none accomplishes nothing at all — you have asked receivers to report to nobody, and you will move to enforcement blind.

Those reports are how you discover the invoicing tool nobody documented. Raw DMARC XML is unpleasant to read, so point rua at a mailbox you can stand to check and, if you are enforcing at any scale, use a report parser.

Moving past p=none

This is where most domains stop, and a domain sitting at p=none indefinitely is not protected. The policy says: tell me about mail that fails, then deliver it anyway. A forger's message still lands in the inbox.

The path to enforcement:

  1. Sit at p=none for two to four weeks and read the aggregate reports. You are looking for your own senders that fail — every one of them is mail that will start being quarantined when you tighten.
  2. Fix them. Usually that means adding a missing include to SPF or turning on DKIM for that platform.
  3. Move to p=quarantine once the only failures left are ones you do not recognise. Failing mail now goes to spam rather than the inbox.
  4. Move to p=reject after a few more clean weeks. Failing mail is now refused outright, which is the point of the exercise.

Do not skip from none to reject on a domain whose reports you have not read. That is how a company discovers, on a Monday morning, that its payroll notifications were being sent by a service nobody had added to SPF.

The pct tag, and why it should not stay

pct= applies your policy to only a percentage of failing mail — pct=25 means three quarters of forgeries are still delivered. It exists as a ramp: set p=quarantine; pct=10, watch, raise it. The failure mode is forgetting it. A record reading p=reject; pct=10 looks like strict enforcement in a summary and is doing almost nothing. Once your reports are clean, remove the tag or set pct=100.

One more tag worth setting deliberately: sp= controls the policy for subdomains. If you do not set it, subdomains inherit your main policy — which is usually what you want, since invoices.yourdomain.com is a favourite of spoofers. A record with p=reject; sp=none leaves every subdomain unprotected.

A working set of records

For a domain sending through Google Workspace and Amazon SES, fully rolled out:

Name: @
Type: TXT
Value: v=spf1 include:_spf.google.com include:amazonses.com -all

Name: google._domainkey
Type: TXT
Value: v=DKIM1; k=rsa; p=MIIBIjANBgkq...   (generated by Google)

Name: _dmarc
Type: TXT
Value: v=DMARC1; p=reject; rua=mailto:[email protected]

Run your own domain through the checker to confirm all three resolve as you expect.

What authentication does not fix

Worth being clear about the boundary. Authentication decides whether receivers trust mail that claims to be from you. It has no bearing on whether the addresses you send to exist. A perfectly authenticated campaign to a stale list still hard bounces, and those bounces still damage the sender reputation your DNS records were protecting.

The two work together: authenticate your domain so receivers trust you, and verify your addresses so you are not sending to mailboxes that were closed two years ago.

HA
Hussam Abd
Founder & Engineer

Hussam Abd is the founder and engineer behind BounceShift. He has been building software since 2013 — full-stack engineering, then founding and leading engineering teams, including CTO and founder roles at MSAAQ and product and front-end work on customer-support tooling at Hsoub. He built BounceShift to make email verification that is accurate and honest about what it cannot know.

Related

Keep reading

How to Set Up SPF, DKIM and DMARC (and Fix Them When They Break)

Get 100 free validations to test our service. No credit card required.

Start free trial