A domain can display flawless SPF, DKIM and DMARC and still be, in practice, as spoofable as one with no authentication at all. The reason is three characters long: p=none. As long as the policy keeps that value, receiving servers report abuse without ever blocking it. Tightening toward quarantine and then reject is the only step that truly protects, and aggregate reports are what makes that step possible without accidentally sending legitimate mail to the junk folder.
What p=none protects, and what it does not
DMARC (RFC 7489) adds two guarantees on top of SPF and DKIM. The first is alignment: the domain shown in the From: header must match the domain validated by SPF or by DKIM. The second is a published policy telling the receiving server what to do with a non-aligned message. That policy is carried by the p tag.
With p=none, the instruction sent to receivers is explicit: observe, but do not block. A message spoofing the domain therefore passes exactly as it did before DMARC was installed. The only gain at this stage is visibility: the domain starts receiving reports describing who sends in its name. That collection step is essential, but mistaking it for protection is the most common error in deliverability.
Anatomy of an aggregate report
An aggregate report (the rua tag) is an XML file that a receiving server generates, by default once a day, for a given domain. It contains no message content: only source IP addresses, volumes, and the SPF, DKIM and DMARC evaluation results. Here is an excerpt reduced to the essentials.
<record>
<row>
<source_ip>203.0.113.24</source_ip>
<count>148</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>exemple.fr</header_from>
</identifiers>
<auth_results>
<spf><domain>mailer.prestataire.com</domain><result>pass</result></spf>
</auth_results>
</record>This block describes 148 messages sent from a single IP, presenting themselves as exemple.fr. SPF passes, but on the domain mailer.prestataire.com: there is authentication, not alignment. DKIM fails. DMARC result: not aligned. Under p=none the disposition stays none and the message goes through anyway. Under p=reject, those 148 messages would be refused. The whole question is whether they are legitimate.
| XML field | What it reveals | Use in diagnosis |
|---|---|---|
source_ip | The real sending address | Identify the sending service |
count | The volume over the period | Prioritise high-volume flows |
policy_evaluated/dkim|spf | The alignment DMARC kept | Spot alignment failures |
header_from | The displayed domain | Confirm identity spoofing |
auth_results | The domain actually authenticated | Tell a legitimate provider from fraud |
An aggregate report does not say whether an email arrived: it says who is allowed to write in the domain’s name, and who steps outside that.
Reading the reports to find legitimate senders that fail
By hand, the reports become unreadable past a few providers. The useful reflex is not to inspect everything, but to isolate one category: non-aligned, high-volume sources. Those are the ones that will break on tightening if they are legitimate, or that prove fraud if they are not. A few lines of script are enough to extract that list from a decompressed report.
import xml.etree.ElementTree as ET
tree = ET.parse("rapport_agrege.xml")
for rec in tree.findall(".//record"):
dkim = rec.findtext("row/policy_evaluated/dkim")
spf = rec.findtext("row/policy_evaluated/spf")
if dkim != "pass" and spf != "pass": # non aligné sur les deux
ip = rec.findtext("row/source_ip")
count = rec.findtext("row/count")
frm = rec.findtext("identifiers/header_from")
print(f"{count:>6} {ip:Each output line is a decision to make: does this IP belong to a service used legitimately (marketing router, billing, CRM, site form) or to a third party spoofing the domain? For the former, the answer is to fix their SPF or enable their DKIM signature before tightening. For the latter, tightening is precisely the goal: they will be blocked.
Ramping up: from none to reject
The move is never made in one jump. It follows a ramp, where each step is held long enough for an aggregate report to confirm the absence of any legitimate sender in failure. The pct tag historically applies the policy to only a fraction of messages, to observe the effect before generalising it.
; palier intermédiaire : quarantine sur un quart du trafic
_dmarc.exemple.fr. IN TXT "v=DMARC1; p=quarantine; pct=25; rua=mailto:[email protected]; adkim=s; aspf=s"| Policy | Receiver action | When to adopt it |
|---|---|---|
p=none | No blocking, reports only | Initial collection, inventory of sources |
p=quarantine | Message filed as spam | All legitimate sources are aligned |
p=reject | Message refused at delivery | No legitimate failure across several report cycles |
A realistic pace holds each step one to two weeks: p=none, then p=quarantine; pct=25, then pct=100, then p=reject. The ongoing revision of the specification tends to discourage pct in favour of a gradual rollout by subdomains and a distinct policy through the sp tag; the principle stays the same, only the mechanics of the step change.
Failure reports (ruf) and feedback loops
Alongside aggregate reports, the ruf tag requests failure reports, message by message, in ARF format (RFC 5965). They are more precise, but rarely sent: most large receivers suppress them so as not to pass on personal data. Relying on them means giving up visibility.
Watch point. Failure reports can contain headers, or even excerpts of real messages. Pointing ruf at a shared mailbox or an analysis provider exposes that data. Day-to-day deliverability monitoring is better served by the large operators' postmaster dashboards and by aggregate reports, which carry no content.
Key takeaways
DMARC only protects from quarantine onward. The path to reject is a matter of reading, not configuration: each step waits for an aggregate report to confirm that no legitimate source is dropping. The sequence is always the same: collect at none, align the real senders spotted in the reports, tighten in steps, verify, refuse. A domain left for months at p=none is not being secured: it is on hold.
On the domains I work with, the blocker is almost never technical: it is the fear of breaking some forgotten flow. My rule has become simple: I never tighten a step without two clean cycles of aggregate reports in front of me. That discipline has saved me more than one Monday morning spent working out why the invoices stopped going out. Tightening DMARC is not risky in itself; doing it blind is. — Simon Janvier
Going further
DMARC specification and the aggregate report schema: RFC 7489 (IETF).
