Deliverability

SPF, DKIM and DMARC without the acronym soup

SPF checks the sending server, DKIM checks the message, and DMARC ties both to the address your reader actually sees. How the three fit together.

By Dana Okafor ·

SPF, DKIM and DMARC answer three different questions, and most explanations of them jump straight to record syntax without saying what the questions are. SPF asks whether the server that sent a message is allowed to send for that domain. DKIM asks whether the message was signed by a key that domain published, and whether it changed on the way. DMARC asks what a receiving inbox should do when neither of those lines up with the domain in the From: address, the one a human reader actually sees, and where to send a report about it.

A message can pass SPF and pass DKIM and still fail DMARC, because neither SPF nor DKIM looks at the From: address at all. That one fact is the reason a thousand posts explaining these three letters still leave readers unable to act on them.

Key Takeaways

  • SPF checks the address in the message’s return path, not the one your reader sees.
  • DKIM checks a cryptographic signature, and only proves the signing domain vouches for the message, not that it’s the domain in the From: line.
  • DMARC exists because of that gap. It checks whether the SPF or DKIM domain matches the visible From: domain, a check called alignment, and only counts as a pass when one of them does.
  • p=none publishes DMARC in monitoring mode: nothing gets blocked, but you start receiving reports. Move to p=quarantine and then p=reject only once those reports show nothing legitimate breaking.

SPF: which servers may send

SPF, Sender Policy Framework, is a DNS TXT record that lists which mail servers are allowed to send on a domain’s behalf. RFC 7208 defines it. A TXT record is just a line of text published in a domain’s DNS, the same place its other records live; anyone can look one up, no account or login required.

The record looks like this, published as a TXT record at the domain itself:

v=spf1 include:yourprovider.com ~all

include: names another domain whose own SPF record lists the actual sending servers, which is how most people authorize a mail provider without listing raw IP addresses themselves. The last token is what to do about a server the record doesn’t cover: ~all (softfail) says treat it with suspicion, -all (fail) says reject it outright.

Here is the detail that trips people up: SPF checks the envelope sender, technically the RFC5321.MailFrom, sometimes called the return-path address. That is a header used for routing and bounce handling, and it is often invisible in a normal email client. It is frequently a different domain from the one in the From: line the recipient sees, especially when a message goes out through a third-party sending service. SPF has nothing to say about that visible From: address. A “pass” means the sending server was authorized to use the return-path domain it used. It does not mean the domain your reader sees sent the message.

DKIM: the message was signed by this domain, unaltered

DKIM, DomainKeys Identified Mail, is a digital signature attached to a message. RFC 6376 defines it. The sending system holds a private key and signs the message; the corresponding public key is published in DNS at a selector-based location like selector._domainkey.example.com, where selector is a name the sender chooses. A receiving server fetches that public key and checks the signature.

A passing signature proves two things: the signed parts of the message, typically the body and headers like Subject and From, have not been altered since signing, and the domain named in the signature’s d= tag vouches for the message. It does not prove who wrote the message, and the d= domain is not required to match the visible From: domain either. A signature can be entirely valid and still be signed by a domain the reader has never heard of.

The gap both of them leave open

SPF and DKIM each authenticate a domain. Neither one is required to be the domain a reader sees in their inbox. A message sent through a bulk mail platform can pass SPF against the platform’s return-path domain and pass DKIM against the platform’s signing domain, while the From: address reads newsletter@yourcompany.com. Both checks pass. Nothing has verified that yourcompany.com had anything to do with sending it.

That is the gap DMARC closes, and it is the single most useful idea in this whole subject. DMARC does not add a third, independent check. It takes the SPF and DKIM results you already have and asks one more question: does either of those authenticated domains align with the domain in the From: header?

Alignment in its default, relaxed form means the two domains share the same organizational domain: mail.yourcompany.com aligns with yourcompany.com, but sendplatform.com does not, even if sendplatform.com passed SPF cleanly. DMARC passes if either SPF or DKIM produces an aligned result; it does not require both. So the fix for the scenario above is not really about SPF or DKIM being wrong. It’s about making sure whichever one you’re relying on is checked against your own domain, which usually means sending through infrastructure that signs DKIM as you, or that uses your own domain in its return path.

What DMARC actually does

A DMARC record is a DNS TXT record published at _dmarc.example.com, at the organizational domain rather than a sending subdomain. In its simplest form:

v=DMARC1; p=none; rua=mailto:reports@example.com

p= is the policy: what a receiving mailbox provider should do with a message that fails DMARC (fails alignment on both SPF and DKIM). rua= is an address to receive aggregate reports, daily summaries showing which servers sent mail claiming your domain and whether each one passed alignment. There is also an ruf tag for per-message forensic reports, but very few large mailbox providers send them in practice, so most DMARC setups rely on rua alone.

The three policies

PolicyWhat it tells a receiving inbox
p=noneDo whatever you’d otherwise do with this mail. Just send me a report.
p=quarantineTreat a failing message as suspicious, typically routing it to spam.
p=rejectRefuse a failing message outright.

p=none changes nothing about delivery. It is there so you can see, from the reports, exactly which servers send mail as your domain and whether each one is aligned, before you ask receiving providers to start blocking anything. Publishing DMARC at p=none is not optional caution, it is the only way to find out what would break if you tightened it. Skipping straight to p=reject risks silently dropping a service you forgot was sending on your behalf, like a marketing list you set up two years ago.

p=none is a starting point, not a destination, because it protects nothing. A domain sitting at p=none indefinitely is still fully spoofable: anyone can send a message with your domain in the From: line, and the worst that happens is you get a report about it after the fact. Once the aggregate reports show every legitimate sender aligning cleanly, usually after a couple of weeks of watching, moving to p=quarantine and then p=reject is what actually stops a forged message from reaching an inbox looking like it came from you.

Reading the reports

Aggregate reports arrive as compressed XML files, one from each participating mailbox provider, usually daily. They are not built for a human to read directly; most DMARC monitoring tools exist purely to parse and summarize them. Each report lists the sending IP addresses that claimed your domain in that window, how many messages each sent, and whether each one passed SPF, DKIM and alignment.

What you are looking for in the early weeks is not enforcement, it’s inventory. A report full of unfamiliar sending sources is a list of everything currently sending as your domain that you did not know about: an old marketing tool, someone’s forwarded mail. Each one either needs its own aligned DKIM signature, needs to send from a domain that’s in your SPF record, or needs to stop sending as your domain at all. That inventory step is why the order below starts with SPF and DKIM and treats DMARC as the thing that comes after, not first.

The order that doesn’t lock you out of your own mail

  1. Publish SPF covering every server and service that legitimately sends as your domain, including anything you might forget: a support desk, a form tool, a billing system.
  2. Publish DKIM for each of those same services. Most providers give you their own selector and key to publish; a domain can carry more than one DKIM selector at once.
  3. Publish DMARC at p=none, with rua pointed at an inbox someone actually reads. This is the step that starts producing visibility, and it changes nothing about delivery yet.
  4. Watch the reports for a couple of weeks. Confirm every legitimate source shows up aligned. Fix or remove anything that doesn’t.
  5. Move to p=quarantine. Failing mail starts landing in spam instead of the inbox, which is a much safer place to discover a missed sender than a hard bounce.
  6. Move to p=reject once quarantine has run clean for a while.

Gmail and Yahoo require a DMARC record to exist with a policy of at least p=none, and the From: domain aligned with SPF or DKIM, for anyone sending in bulk to their users. Both providers say p=none satisfies the requirement to start: Google’s bulk sender guidelines and Yahoo’s sender best practices both list “at least p=none” as the bar, not p=reject. Getting to p=none with everything aligned clears that requirement. Tightening further from there is about protecting your own domain from being spoofed, not about qualifying to send. Gmail, Yahoo and Microsoft’s bulk sender rules covers the rest of what each provider asks for beyond authentication.

If you send through EmailFig

Every sending domain you add on EmailFig gets seven DNS records generated for you: three DKIM CNAME records, the two records for a custom MAIL FROM subdomain (an MX, and the SPF TXT that lets SPF authenticate that address), a DMARC record published at p=none, and a tracking CNAME that puts your tracked links on your own domain. You copy them into your DNS provider once; EmailFig checks them for you afterward and shows the domain as Verified once every record is live. Publish DNS records at your domain provider walks through what each one is doing, and Publish DNS records at your domain provider covers the actual click path at five common providers. If a domain sits in Pending longer than expected, or drops back to it after previously verifying, Domain verification statuses and why a domain goes back to pending cover the common causes.

EmailFig generates a p=none record because that’s the only starting point that doesn’t risk your existing mail. Tightening it to quarantine or reject, once you’ve watched your own DMARC reports and confirmed nothing legitimate is failing, is a change you make directly at your DNS provider, the same way you’d edit any other DNS record.

None of this requires understanding certificates or cryptography to act on. It requires publishing three TXT records in the right order and reading a report before you tighten anything. The order matters more than the acronyms do.

Authentication is the technical floor of deliverability, not the whole of it; see email deliverability for people who aren’t experts for how it fits alongside bulk sender rules, bounce and complaint rates, and list hygiene.

Keep reading

All posts →