All articles

Email Authentication

DMARC Explained: The Missing Piece After SPF and DKIM

SPF and DKIM check whether a message is authorized and unmodified. DMARC is what tells receiving servers what to do about it, and it's the record most domains set up wrong or not at all. Here's the full anatomy of a DMARC record and how to roll one out safely.

July 8, 2026

SPF authorizes sending servers. DKIM verifies message integrity. Neither one, by itself, tells a receiving mail server what to actually do when a message fails those checks. That's DMARC's job, and it's also the record most domains either skip entirely or configure once and never touch again.

What DMARC actually adds

DMARC (Domain-based Message Authentication, Reporting, and Conformance) sits on top of SPF and DKIM rather than replacing them. It does two things neither of the other two records can do on its own:

  1. It defines a policy. What should a receiving server do with a message that fails both SPF and DKIM? Nothing? Send it to spam? Reject it outright? Without DMARC, that decision is entirely up to each receiving server's own heuristics.
  2. It requires alignment. A message can pass SPF and still be fraudulent if the domain that passed SPF doesn't match the domain the recipient actually sees in their inbox. DMARC closes that gap.

A DMARC record lives at a fixed, predictable location:

_dmarc.example.com.  TXT  "v=DMARC1; p=quarantine; rua=mailto:[email protected]; pct=100"

Unlike DKIM, there's only ever one DMARC record per domain (per subdomain scope), and unlike SPF, there's no ambiguity about where it lives. It's always _dmarc. prefixed to the domain in question.

Anatomy of a DMARC record

v=DMARC1 — version tag, required, always first.

p= — the policy applied to the domain itself. Three values:

  • none: take no action based on DMARC, just collect reports. This is the required starting point for any new deployment.
  • quarantine: deliver failing messages, but flag them (typically routed to spam).
  • reject: refuse failing messages outright, at the SMTP level.

sp= — a separate policy for subdomains, if you want it to differ from p=. Without sp=, the p= policy applies to subdomains too. This matters because a domain that sends mail from example.com but never from any subdomain is still exposed if an attacker sends spoofed mail from random.example.com and no subdomain policy exists to catch it.

pct= — the percentage (0 to 100, not 1 to 100 — pct=0 is valid and means something different from p=none) of failing messages the policy applies to. Defaults to 100 if omitted, and has no effect under p=none. Ramping this up gradually (pct=10, then 25, then 100) is the classic way to move to quarantine or reject cautiously: messages a low pct excludes from reject fall back to quarantine rather than being let through untouched. Real-world support for pct has always been inconsistent — the original spec never resolved whether the percentage applies to the domain's whole mail stream or just the messages that fail, and receivers answered that differently. That ambiguity is why pct was dropped in RFC 9989, the 2026 update to the spec (more below), in favor of a simple t=y/t=n testing flag. pct still works today and will for years, but it's on its way out.

rua= — the mailto address for aggregate reports: XML summaries showing every source that sent mail claiming to be from your domain, and whether each one passed or failed. A separate ri= tag can request a non-default reporting interval (in seconds, default 86400), but in practice almost every receiver batches daily regardless of what's requested. This is the report type worth setting up on every domain.

ruf= — the mailto address for forensic reports: per-message failure reports with more detail, sent as failures happen rather than batched daily. Far fewer providers send these, and they can include fragments of message content, which is a privacy consideration some providers deliberately avoid by not supporting ruf= at all. A separate fo= tag controls when they fire: 0 (default) only when a message fails both SPF and DKIM alignment, 1 when it fails either one, or d/s to limit reporting to just a DKIM or SPF failure.

adkim= and aspf= — alignment mode, r (relaxed, the default) or s (strict). Relaxed alignment accepts a match on the organizational domain (mail.example.com aligns with example.com). Strict alignment requires an exact match. Most domains use relaxed alignment, since it's common for a subdomain to send the authenticated mail while the visible From: uses the root domain.

RFC 7489, which defines all of the tags above, was obsoleted in May 2026 by RFC 9989 (the core protocol), RFC 9990 (aggregate reporting), and RFC 9991 (failure reporting) — jointly nicknamed DMARCbis — which moved DMARC onto the IETF Standards Track as a Proposed Standard. Everything above still works, and the new specs are backward compatible for a transition period expected to run for years, but a couple of things are shifting: pct is deprecated in favor of t=, and a new np= tag now explicitly sets the policy for subdomains that don't exist in DNS at all, closing an ambiguity the old spec left open.

Why alignment is the part people miss

SPF and DKIM authenticate against different things. SPF checks the envelope sender (MAIL FROM, invisible to the recipient). DKIM checks the domain named in the d= tag of its signature. Neither one is required to match the From: header address the recipient actually sees.

That gap is exploitable: a message can pass SPF using a legitimately authorized server while displaying a completely different From: address to the recipient. DMARC alignment closes it by requiring that the domain which passed SPF or DKIM matches the visible From: domain (exactly, under strict mode, or at the organizational level, under relaxed mode). A message needs to pass SPF or DKIM and align to pass DMARC. Passing SPF alone, with a mismatched From: domain, is not enough.

The rollout path: none → quarantine → reject

Publishing p=reject on day one, before you know what's actually sending mail as your domain, is the most common way DMARC deployments break things. Legitimate mail you didn't know about (a marketing tool, a support platform, a forwarding rule, an internal script) fails and gets rejected outright, with no warning.

The safer sequence:

  1. p=none, rua= set. Publish the record, collect aggregate reports, take no enforcement action. Do this for at least a couple of weeks.
  2. Review the reports. They show every source claiming to send as your domain, and whether each one passed SPF, DKIM, and alignment. Anything failing that you recognize as legitimate needs SPF or DKIM fixed for that source before you tighten the policy.
  3. p=quarantine, low pct=. Move to quarantine, initially covering a small percentage of traffic, and watch for legitimate mail landing in spam.
  4. Ramp pct= to 100, then move to p=reject. Once quarantine at full coverage looks clean, reject is the final, strictest state.

Skipping straight to reject is the deployment equivalent of turning on a strict firewall rule without checking what traffic currently flows through it.

Reading the reports

Aggregate reports arrive as compressed XML, batched daily, from every major mail provider that received a message claiming to be from your domain. The spec calls for GZIP compression (a .xml.gz attachment), and most senders follow it, but not universally: Google, Microsoft, and Yahoo, among others, commonly send a .zip attachment instead. Anything parsing these reports needs to handle both. They are not meant to be read directly. A single report from a busy domain can list dozens of sending sources, and manually parsing the raw XML to figure out which ones are legitimate and which aren't is not a practical way to spend an afternoon.

MyDMARC parses these reports and turns them into a readable view: which services are sending on your behalf, which are passing, which are failing, and whether it looks safe yet to move to a stricter policy. This is generally the fastest path from "I just published a DMARC record" to "I know exactly what's sending mail as my domain."

Checking a DMARC record

dig TXT _dmarc.example.com

If nothing comes back, the domain has no DMARC policy at all, meaning receiving servers fall back entirely to their own judgment for messages that fail SPF or DKIM. MyDMARC's DMARC checker will validate an existing record's syntax and flag missing or malformed tags.

How this connects to SPF and DKIM

DMARC doesn't work in isolation. It needs at least one of SPF or DKIM configured correctly, and alignment depends on both being set up per sending source. For how SPF evaluation works, see SPF Records Explained; if you haven't set one up yet, start with Your Domain Has No SPF Record. For DKIM, see DKIM Records Explained. For the full picture of how all three fit together as TXT records, see DNS TXT Records: SPF, DKIM, and DMARC All Live Here.

How DNS monitoring fits in

A DMARC policy that quietly changes is easy to miss and consequential either way. A policy weakened from reject back to none reopens the door to spoofing without any visible symptom. A policy accidentally tightened to reject before reports have been reviewed can start silently rejecting legitimate mail.

OneDollarDNS monitors your _dmarc TXT record alongside every other record on your domain and alerts you the moment its value changes, whether that's a policy shift, a new reporting address, or the record disappearing entirely.

Monitor your DNS for $1/month

OneDollarDNS watches your DNS records and alerts you the moment anything changes.

Get started free