DomainOpsDomainOps

DMARC policy: the migration path from p=none to p=reject

How to move your DMARC policy from p=none to p=reject safely: rua reports, pct= staged rollout, SPF/DKIM alignment, and why domains get stuck at none.

DomainOps Team··5 min read

A DMARC policy tells receiving mail servers what to do with messages that claim to be from your domain but fail authentication: do nothing (p=none), put them in spam (p=quarantine), or refuse them (p=reject). Publishing the record is a five-minute DNS change. Getting from p=none to p=reject without junking your own legitimate mail is the actual project — and it's the project most domains never finish. The majority of DMARC records in the wild sit at p=none indefinitely, which provides monitoring data and satisfies checkbox requirements but blocks precisely zero spoofed mail.

What a DMARC record contains

DMARC lives in a TXT record at _dmarc.<yourdomain>:

txt
_dmarc.example.com.  TXT  "v=DMARC1; p=quarantine; pct=25; rua=mailto:[email protected]; adkim=r; aspf=r; sp=reject"
TagMeaning
p=Policy for the domain: none, quarantine or reject.
sp=Policy for subdomains, if different from p=.
pct=Percentage of failing mail the policy applies to (default 100).
rua=Where to send aggregate reports.
ruf=Where to send failure (forensic) reports — sparsely supported.
adkim= / aspf=DKIM/SPF alignment mode: r relaxed (default) or s strict.

A message passes DMARC if either SPF or DKIM passes and aligns — meaning the domain that passed matches the domain in the visible From header. With relaxed alignment, an organisational-domain match suffices (mail.example.com aligns with example.com); with strict, it must match exactly. Relaxed is the right default for almost everyone; strict alignment mostly creates surprises when a subdomain or an ESP return-path enters the picture.

The either/or is the crucial design decision. SPF breaks under forwarding; DKIM doesn't. So a well-set-up sender passes DMARC via DKIM even when a forwarder wrecks SPF — which is why getting DKIM signing right for every sending service is the real prerequisite for enforcement.

Step one: p=none with reporting

Start here, but understand what it is: a monitoring tap, not a policy.

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

The rua address receives aggregate reports — XML files, typically daily, from each participating receiver (Google, Microsoft, Yahoo and most large providers send them). Each report says: these IPs sent mail as your domain, this many messages, here are the SPF/DKIM/alignment results. Raw XML is miserable to read at volume, so most teams feed it into a parsing tool.

ruf failure reports were meant to provide per-message forensic detail, but privacy concerns mean very few receivers send them. Don't build your plan around ruf.

Spend a few weeks (a month is typical; longer if you have monthly billing runs or quarterly newsletters) answering one question from the aggregate data: what legitimate sources are failing alignment? Common finds: the CRM nobody mentioned, an office printer that emails scans, a regional office's marketing tool, internal apps relaying through a provider without DKIM. Fix each one — usually by setting up DKIM signing with the service, occasionally by adding it to SPF with an aligned return-path.

Step two: quarantine, staged with pct=

Once the report data shows your known-legitimate sources passing, move to enforcement gradually:

txt
v=DMARC1; p=quarantine; pct=10; rua=mailto:[email protected]

pct=10 asks receivers to apply quarantine to only 10% of failing messages, leaving the rest at none. Ratchet it — 10, 25, 50, 100 — over a few weeks, watching the reports between each step. If a forgotten sender surfaces, only a fraction of its mail was junked while you fix it. (One quirk worth knowing: pct applies the next lower policy to the remainder, so p=reject; pct=50 quarantines the other half rather than ignoring it.)

A note on quarantine as a destination: it means "treat with suspicion", which in practice means the spam folder. Users do occasionally fish things out of spam; nothing comes back from a reject. That makes quarantine the right place to pause and soak for a while.

Step three: reject

txt
v=DMARC1; p=reject; rua=mailto:[email protected]; sp=reject

p=reject is the destination — it's the only policy that actually stops spoofed mail from being delivered. Keep rua reporting forever: new sending services will be added by people who've never heard of this project, and the reports are how you find out before the complaints do.

Set sp= deliberately. Without it, subdomains inherit p=, which is usually what you want — attackers happily spoof billing.example.com if the apex is locked down but subdomains aren't. If specific subdomains send mail through services you can't align yet, that's a reason to fix those services, not to leave sp=none as a permanent loophole.

Why domains get stuck at p=none — and what it costs

The stall pattern is consistent. p=none got published to satisfy a deliverability checklist or the Google/Yahoo bulk-sender requirements (which mandate DMARC at minimum p=none for senders of 5,000+ messages/day). The aggregate reports go to a mailbox nobody reads. Moving to enforcement requires chasing down every team's sending tools, which is organisational work, not DNS work. So it never happens.

What it costs:

  • Spoofing protection: none. At p=none, a forged invoice from your exact domain sails through DMARC with no policy consequence. Domain spoofing for phishing and invoice fraud is precisely what enforcement exists to stop.
  • Deliverability signal: weakened. Receivers increasingly factor enforcement into reputation; an enforced policy is a stronger trust signal than a monitoring-only one.
  • The reports rot. The visibility p=none was supposed to provide only has value if someone acts on it.

The honest framing: p=none is a starting line that most domains have mistaken for a finish line.

DomainOps monitors DMARC records across your domain portfolio — policy, alignment settings, and whether the record breaks or gets downgraded — alongside SPF and DKIM checks, with alerts when something changes. To see where your domain stands today, run the free email auth audit.

dmarcemaildns