toolhq.io

All posts
6 min readby Jameel Haider

Rolling out a DMARC policy safely, from p=none to reject

The fastest way to block your own payroll emails is to publish p=reject on day one. DMARC enforcement is safe only after you have watched who sends mail as your domain, and the whole rollout is designed around earning that visibility before you turn on blocking.

What DMARC actually enforces

DMARC sits on top of SPF and DKIM. It checks that at least one of them passes and that the passing domain aligns with the domain in the visible From header. The policy you publish tells receivers what to do when that check fails: none means do nothing, quarantine means treat as suspicious, usually spam, and reject means refuse the message outright. The goal of a rollout is to reach reject without any legitimate sender failing alignment.

Step one: p=none and listen

Start in monitoring mode. This changes nothing about delivery and only asks receivers to send you reports.

v=DMARC1; p=none; rua=mailto:dmarc-reports@yourdomain.com; fo=1

The rua address collects aggregate reports: daily XML summaries from receivers listing every source that sent mail claiming to be your domain, and whether each passed SPF, DKIM, and alignment. This is the single most important part of the rollout. You cannot safely enforce until you recognise every legitimate sender in those reports.

Step two: read the reports and fix alignment

Aggregate reports are verbose XML, but they answer one question: which of these sources are really us. You will almost always find legitimate senders failing alignment:

  • A marketing platform signing with its own domain, so DKIM passes but does not align.
  • A support desk or invoicing tool nobody remembered was sending as you.
  • A subdomain used by a CI system or monitoring tool.

For each legitimate source, fix the cause: add it to SPF, set up DKIM signing with an aligned domain, or move it onto a subdomain with its own policy. Do not advance until the only failures left are sources you are happy to block.

Step three: ramp with pct

When you move to quarantine, apply it to a fraction of mail first using pct:

v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc-reports@yourdomain.com
StagePolicypctWhat it does
Monitornonen/aReports only, no action
Early enforcequarantine25A quarter of failing mail to spam
Mid enforcequarantine100All failing mail to spam
Full enforcereject100Failing mail refused

The pct tag applies the policy to that percentage of messages that fail, so a low percentage limits the blast radius while you watch reports for anything unexpected. Raise it in steps, each held long enough to see a few report cycles, which usually means several days per step rather than hours.

Step four: reject

Once quarantine at 100 percent runs clean for a couple of weeks with no legitimate sender failing, move to p=reject. At this point mail that fails alignment is refused at the receiver, which is exactly the protection you wanted, and because you got there slowly you already know it will not touch real mail.

Common breakage to expect

Mailing lists and forwarders are the usual casualty: they rewrite or re-send mail in ways that break SPF and sometimes DKIM. ARC exists to preserve the original authentication through a forwarding hop, but support is uneven, so expect some list traffic to fail and decide deliberately whether you care.

Build each stage of the record, including the reporting addresses and pct value, with the DMARC Generator, and only advance a stage once the reports from the previous one are clean.