Skip to content
[menu][close]

FOUNDRYNETNo. 001SEPTEMBER 2026PROVENANCE

DMARC, SPF and DKIM: Verify the Origin of Email at the Edge

SPF, DKIM and DMARC are three DNS-published records that let a receiving mail gateway decide whether a message really left the domain named in its From header. Together they form a provenance system for mail, with precise limits on what they prove.

On this page

01 What SPF, DKIM and DMARC prove about a message

A message arrives at the MX with a From header naming example.net. Nothing in SMTP checks that header; anyone can write it. SPF, DKIM and DMARC are three DNS records the domain owner publishes so the receiving edge can test the claim against facts a forger does not control: the owner's sending hosts, signing key and failure policy.

SPF covers the connecting host, DKIM a signature made with the domain's published key, and DMARC whether either result lines up with the From domain the reader sees. The verdict is a statement about origin, like forward-confirmed DNS on a claimed crawler, and the lane on proving who sent what keeps one distinction central: a verified origin says nothing about whether the content is honest.

02 SPF: which hosts may send for a domain

RFC 7208 defines the Sender Policy Framework. The domain owner publishes a TXT record beginning v=spf1 listing hosts permitted to send with that domain in the envelope MAIL FROM. Mechanisms include ip4, ip6, a, mx and include; the record ends with -all (fail) or ~all (softfail).

Section 4.6.4 of RFC 7208 sets the rule that catches most deployments: terms that cause DNS queries (include, a, mx, ptr, exists, redirect) are limited to ten across the whole evaluation, nested includes counted. Exceed it and the result is permerror, which receivers treat as no SPF. SPF never looks at the From header.

03 DKIM: a signature over headers and body

RFC 6376 defines DomainKeys Identified Mail. The sender hashes the body into bh= inside a DKIM-Signature header, then signs that header plus the headers listed in h= (which must include From) with a private key. The signing domain is d=, the selector s=. The receiver fetches the public key from the TXT record at selector._domainkey.domain and checks the signature.

Selectors let one domain hold many keys. A valid signature proves two things and no more: someone holding the key at that selector signed the message, and the signed headers and body are unchanged since. It does not prove which host sent it.

04 DMARC: alignment with the From domain and a policy

RFC 7489 defines Domain-based Message Authentication, Reporting and Conformance. DMARC ties the other two to the visible From domain. The record lives at _dmarc.example.net and starts v=DMARC1. A message passes if SPF passed and the envelope domain aligns with the From domain, or DKIM verified and d= aligns with it.

In relaxed alignment, the default, the Organizational Domains must match, so mail.example.net aligns with example.net; strict (aspf=s, adkim=s) requires an exact match. The p= tag sets the policy for failures: none (deliver, report only), quarantine (treat as suspect) or reject (refuse at SMTP). sp= covers subdomains, pct= applies the policy to a fraction of failing mail, rua= receives daily aggregate XML reports and ruf= per-message failure reports, which few large receivers send.

DMARC evaluation at the receiving edgeDMARC EVALUATION AT THE RECEIVING EDGE01Accept SMTPsession, noteconnecting IP02Evaluate SPFon envelopedomain03Verify anyDKIMsignatures04Testalignmentwith Fromdomain05Apply p=policy, writereport rowDMARC evaluation at the receiving edgeDMARC EVALUATION AT THE RECEIVING EDGE01Accept SMTP session, noteconnecting IP02Evaluate SPF on envelope domain03Verify any DKIM signatures04Test alignment with From domain05Apply p= policy, write report row
DMARC needs only one aligned pass.

05 What the receiving edge can prove, and forwarding

The check runs at the MX, the first system to accept the SMTP session. Where the MX is a pool of gateways behind a virtual address, each gateway still sees the true connecting address, so a VIP fronting a pool of real servers applies unchanged. Nothing downstream should re-evaluate SPF; from the second hop the connecting address is your own gateway.

A DMARC pass proves the domain owner authorised the sending path or signed the message, and that signed content arrived intact. It does not prove the display name is honest, that a lookalike domain is related, or that an attachment is safe. It is a supplicant proving itself before the port opens: a credential, not a verdict on the traffic that follows.

Forwarding breaks SPF by construction: the forwarder connects from an address the original record does not list. DKIM survives unless the forwarder alters the message; lists that add a footer break it. ARC, RFC 8617, addresses this: a forwarder records the results it saw in ARC-Authentication-Results, signs the message in ARC-Message-Signature and seals the chain in ARC-Seal, so a receiver that trusts the sealer can accept what would otherwise fail. RFC 8617 is Experimental; the trust decision is the receiver's own. BIMI, a brand logo beside authenticated mail, requires quarantine or reject.

Four mechanisms, four facts.
MechanismWhat it provesFailure modeSurvives forwarding
SPF (RFC 7208)Connecting host may send for the envelope domainOver 10 lookups; an unlisted relayNo; the forwarder's IP is not listed
DKIM (RFC 6376)Key holder signed headers and body, unchanged sinceRewritten subject or added footerYes, unless signed content is modified
DMARC (RFC 7489)An aligned pass exists for the From domainVendor using its own envelope and d= domainsOnly as far as DKIM survives
ARC (RFC 8617)A forwarder saw a pass before modifyingReceiver must choose which sealers to trustBy design; each hop adds a seal

06 Worked example: the records and one aggregate report

The records are plain TXT lookups: example.net with a mail platform included by reference, a DKIM key at selector s1, and DMARC still at p=none.

Reading the three records with dig
$ dig +short TXT example.net
"v=spf1 ip4:203.0.113.0/26 include:_spf.example.com -all"
$ dig +short TXT s1._domainkey.example.net
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC..."
$ dig +short TXT _dmarc.example.net
"v=DMARC1; p=none; rua=mailto:dmarc-agg@example.net; adkim=r; aspf=r; pct=100"

Two weeks later one aggregate report covers a day, rows keyed by source IP. Most show 203.0.113.14 and its neighbours passing SPF and DKIM. One row stands out: 198.51.100.77, 412 messages, SPF fail, no DKIM signature, From example.net. A reverse lookup names a hosted ticketing system adopted around 2024 to send as support@example.net, never added to SPF, never given a key. Under p=reject those 412 messages a day would have bounced. The fix is a DKIM key for that vendor at a new selector with d=example.net, not its address range in SPF, which would authorise every tenant. The report found an unlisted relay before the policy punished it.

07 Rolling DMARC out from p=none to p=reject

  1. Inventory every sender

    List each system that sends as the domain: mail platform, ticketing, marketing, alerts, printers. Route them through one set of outbound relays and block port 25 from anything else.

  2. Publish SPF within the limit

    Write v=spf1 with the fewest lookup terms, counting includes recursively against the ten in RFC 7208. Use ip4 for hosts you run, include only for vendors.

  3. Sign with DKIM everywhere

    Give each sending system its own selector and key with d= set to the From domain. Test by reading Authentication-Results on a mailbox you control.

  4. Publish DMARC at p=none with rua

    Set p=none, a rua= mailbox or parsing service, relaxed alignment. Wait at least two weeks. Add a DKIM key with your domain for each unaligned vendor, or list the host in SPF if it is yours.

  5. Step to quarantine, then reject

    Move to p=quarantine; pct=25, raise pct as reports stay clean, then p=reject. Set sp= for subdomains that never send.

08 Pitfalls: lookups, subdomains, vendors and ruf

  • Blowing the ten-lookup limit. Each vendor include pulls in its own includes; three or four platforms can pass ten. The result is permerror on every message. Flatten to ip4 ranges where a vendor publishes stable space.
  • Forgetting subdomains. A domain at p=reject with no sp= applies reject to subdomains, silently killing one that sends unsigned alerts. A subdomain with no record inherits the parent policy under the RFC 7489 Organizational Domain rule, in both directions.
  • Vendors that align only to themselves. A platform signing with its own d= and envelope domain passes SPF and DKIM and still fails DMARC: nothing aligns with your From. The fix is a key with your domain.
  • Expecting ruf reports. Failure reports carry headers and sometimes bodies, which is personal data, and most large receivers do not send them. Publish ruf= only with a legal basis and a deletion process.

Mail authentication borrows from two other pages in this lane: signed DNS answers, which protect the TXT records these checks read, and a public append-only log of certificate issuance, the model DMARC aggregate reports loosely follow by giving the domain owner a view of who is sending in its name.

09 Questions

What is the difference between SPF, DKIM and DMARC?

SPF (RFC 7208) lists which hosts may send for a domain. DKIM (RFC 6376) is a signature over headers and body, verified with a key from DNS. DMARC (RFC 7489) requires one result to align with the visible From domain and sets the failure policy.

Do I need all three?

DMARC needs only one aligned result, so DKIM alone can pass. In practice publish both. SPF fails on every forwarded message and DKIM breaks on modified ones, so a message often survives on one when the other fails. Each covers the other's gap.

What does a DMARC pass actually prove?

That the message came from a host the domain owner listed, with an aligned envelope domain, or carries a valid signature from a key the owner published, with an aligned signing domain. It proves authorised origin and intact signed content, not that the content is true or safe.

Why does forwarding break SPF but not DKIM?

SPF checks the address of the host connecting to your MX, and a forwarder connects from its own address, which the original record does not list. DKIM checks a signature over the message itself, so it survives any hop that leaves the signed content alone.

How long should I stay at p=none?

Until two consecutive weeks of aggregate reports show no legitimate source failing alignment. For a small domain that is often a month; with many vendors it can be longer. Quarantine with a low pct value is a safe middle step once known senders are fixed.

SPF says which hosts may speak for a domain, DKIM says the message was signed with the domain's key and arrived intact, and DMARC ties both to the From domain and tells the receiving edge what to do when neither holds. Publish all three, start DMARC at none, read the reports until every source is explained, then move to reject. The gateway then holds a verified origin for every message it accepts.