Skip to content

DMARC Alignment Explained: How SPF and DKIM Finally Protect the From Header

SPF authenticates the envelope sender. DKIM authenticates a signing domain. Neither ever looks at the From: header — the only sender identity your users actually see. A mail can pass both checks and still display ceo@example.com to the victim. DMARC is the layer that closes this gap, and alignment is the concept that makes it work. If you only ever internalise one thing about email authentication, make it this article.

As with the companion pieces, the angle is analyst-side: understanding verdicts and checking records, not deploying DMARC for your own domain.


Pre-requisites

Examples use RFC 2606 reserved domains and RFC 5737 documentation IPs.

The spec moved in 2026

DMARC was originally specified in RFC 7489 (2015, Informational). Since May 2026 the current specification is RFC 9989 (Standards Track), with aggregate reporting split into RFC 9990 and failure reporting into RFC 9991 — together they obsolete RFC 7489. Notable changes: the pct= tag was removed (a t= test-mode tag replaces it), a np= tag covers non-existent subdomains, and the organizational domain is now found via a DNS Tree Walk instead of the Public Suffix List. Most blog posts and many records you will encounter still reflect the 2015 spec.


Three Domains, One Mail

Every message carries up to three independent sender domains:

Domain Checked by Visible to the user?
MAIL FROM / Return-Path: (RFC5321.MailFrom) SPF No
d= in DKIM-Signature: DKIM No
From: header (RFC5322.From) DMARC Yes — this is the one that gets spoofed

An attacker controls the first two outright: they send from their own envelope domain (SPF passes) and sign with their own DKIM key (DKIM passes). DMARC's contribution is to ask the question the other two skip: do any of those authenticated domains match the domain in the visible From:?

How DMARC Decides

For the From: domain, the receiver fetches the DMARC policy record and then evaluates:

DMARC passes if at least one of the following holds:

  • SPF gave pass and the SPF-validated domain is aligned with the From: domain, or
  • DKIM verified and the d= domain is aligned with the From: domain.

Both mechanisms passing unaligned counts for nothing; one mechanism passing aligned is enough. This "either/or" is deliberate — SPF routinely breaks on forwarding while DKIM survives it, so requiring both would punish legitimate mail.

Alignment comes in two modes, set per mechanism via the aspf= and adkim= tags (both default to relaxed):

  • Relaxed (r) — the domains must share the same organizational domain: mail.example.com aligns with example.com.
  • Strict (s) — the domains must be identical.

Under RFC 9989 the organizational domain is discovered with a DNS Tree Walk (querying _dmarc. records up the name hierarchy); RFC 7489 used the Public Suffix List for the same purpose.

Reading a DMARC Record

The policy lives in a TXT record at _dmarc.<domain>:

dig +short txt _dmarc.example.com
"v=DMARC1; p=reject; rua=mailto:dmarc-reports@example.com; adkim=s"

If no record exists at the From: domain itself, the receiver falls back to the organizational domain's record — subdomains are covered by default. The tags you will meet:

Tag Meaning
v=DMARC1 Version, mandatory first tag
p= Policy for the domain: none, quarantine, or reject
sp= Separate policy for subdomains
np= Policy for non-existent subdomains (new in RFC 9989)
rua= Where aggregate reports go (mailto: URI)
ruf= Where per-message failure reports go
adkim= / aspf= Alignment mode per mechanism: r (default) or s
t= Test mode t=y — evaluate but don't enforce (replaces pct= from RFC 7489; you will still see pct= in older records)

What the three policies request from receivers:

Policy Domain owner's statement Typical receiver behaviour
p=none No preference — monitoring only Mail delivered normally; failures only show up in reports
p=quarantine Failing mail is suspicious Junk folder / quarantine
p=reject Failing mail is not valid use of the domain SMTP-level rejection

The analyst takeaway: p=none means a spoof of that domain lands in the inbox even though DMARC evaluated fail — the header will read dmarc=fail action=none. A domain's DMARC record tells you how spoofable it is; check it for your own brand domains and for any domain being impersonated in a campaign.


Reading the Verdict in Headers

Your gateway records the DMARC result in Authentication-Results: (RFC 8601) — as always, trust only the instance your own infrastructure stamped. Here is a complete header from a spoofed mail, the case from the intro:

Authentication-Results: spf=pass (sender IP is 203.0.113.25)
 smtp.mailfrom=example.org; dkim=pass (signature was verified)
 header.d=example.org; dmarc=fail action=oreject
 header.from=example.com; compauth=fail reason=000

Read it as a story:

  1. spf=pass smtp.mailfrom=example.org — the attacker's server is authorised to send for the attacker's own envelope domain. Fine.
  2. dkim=pass header.d=example.org — the attacker validly signed with their own key. Also fine.
  3. dmarc=fail header.from=example.com — but neither example.org result aligns with the From: domain the user sees. The spoof is exposed.

header.from always names the domain DMARC judged — the visible one. In Microsoft 365 the action= field shows what the filter did with the failure (oreject = failed under p=reject, and the tenant's configured override was applied instead of an SMTP reject; permerror/temperror = the record could not be evaluated), and compauth is Microsoft's composite verdict that folds DMARC together with its own heuristics — reason=000 means an explicit DMARC fail against an enforcing policy.

A dmarc=none verdict means the From: domain publishes no DMARC record at all: nothing constrains spoofing of it beyond content filtering.


Aggregate Reports (rua) in One Minute

The rua= address receives daily XML summaries from every large receiver: which IPs sent mail claiming the domain, and how SPF/DKIM/alignment evaluated for each source. Two things follow for you as an analyst:

  • If your organisation publishes rua=, those reports are a free detection feed for spoofing campaigns against your own domains — someone should be reading them (in practice via a parsing service, the raw XML does not scale).
  • The reports contain no message content — ruf= failure reports would, but most receivers do not send them for privacy reasons.

Putting All Three Together

The mental model to keep from this series:

  • SPF — "this IP may send mail for the envelope domain" — breaks on forwarding, blind to From:
  • DKIM — "the signing domain vouches for this exact content" — survives forwarding, blind to From:
  • DMARC — "does either authenticated domain align with the visible From:, and what should you do if not"

Only the combination makes From: spoofing detectable — and only an enforcing policy (p=quarantine/p=reject) makes it stoppable.


Check It Yourself

Query the DMARC policy of any domain in your case:

dig +short txt _dmarc.example.com

For a subdomain From:, check the subdomain first and the organizational domain as fallback:

dig +short txt _dmarc.mail.example.com
dig +short txt _dmarc.example.com

A v=DMARC1 answer with p=quarantine or p=reject means spoofs of the domain should not reach inboxes at compliant receivers; p=none or no answer means the visible From: is effectively unprotected.