DMARC · Aggregate reports

Your first DMARC report arrived. Here's how to read it

First published 20 October 2026 · Format facts from the specifications themselves: the report format now lives in RFC 9990 (it moved in 2026 — older guides, and even Microsoft's page, still cite the retired appendix), with DMARC itself in RFC 9989. Reports written in the older RFC 7489 dialect still circulate; this guide teaches both where they differ.

Anatomy — the fields that matter

BlockFields to readMeaning
report_metadataorg_name · date_rangeWHO is reporting and the window (a UTC period — the period mail was processed, not necessarily sent)
policy_publisheddomain · p · sp · pct*The DMARC record they evaluated against — sanity-check it matches what you think you published (*pct exists only in the 7489 dialect; the current spec retired it, with t=y taking its test-mode role — see the consistency note in our rollout guide's series context)
rowsource_ip · count · policy_evaluatedThe sender, the VOLUME (never skip — rows are weighted by count), and the verdict block
policy_evaluateddisposition · dkim · spf · reason?dkim/spf here are pass/fail of ALIGNMENT (the health score). disposition = action applied (none/pass/quarantine/reject). reason is required when a failing alignment got a gentler action than your policy implies (values: local_policy, mailing_list, trusted_forwarder, policy_test_mode, other)
identifiersheader_from · envelope_fromThe domain the world saw vs the envelope domain actually authenticated — daylight between them is the alignment story
auth_resultsdkim: domain/selector/result · spf: domain/resultThe RAW layer, “uninterpreted with respect to DMARC” — whose signature, which selector, what the envelope's SPF really did. Your detective layer.

Teaching specimen: the current spec's own example report shows 123 messages with DKIM pass / SPF fail and disposition pass — DMARC passed on the DKIM leg while SPF failed. If you remember one row shape, remember that one: one aligned pass is enough.

The five-pass protocol (Monday morning, 10 minutes)

  1. Whose, when: note the reporting org and window. A week of reports from multiple receivers is a dataset; one report is an anecdote.
  2. Find yourself: mark rows whose source/auth domains are your known senders (your mailbox provider, your ESPs — the auth_results domain names them). These should show an aligned pass.
  3. Your failures = the fix list: rows from YOUR sources with both evaluated fields fail are misconfigurations you own (a new tool sending unauthenticated, a selector that died — our DKIM guide). Fix list first, strangers later.
  4. Classify the unknowns (auth_results is the lens): your DKIM signature present → your forgotten tool, inventory it · everything fails + your domain only in header_from → spoofing (your future p=reject neutralizes it; see our spoofing guide) · reason says trusted_forwarder/mailing_list → transit damage, expected physics, not an enemy.
  5. Do the count math: total the counts; compute the share of YOUR legitimate volume that aligns. That percentage — not any single scary row — is the input to the enforcement decision (our rollout guide's gates are built from exactly this).

Action matrix — what the combinations ask of you

Evaluated DKIM / SPFSituationAction
pass / anythingAligned via DKIMNone. The resilient leg is doing its job
fail / passAligned via SPF onlyWorks today; note it — forwarding breaks SPF alignment first, so get DKIM signing on that source when you can
fail / fail — your sourceYour sender, unauthenticatedFix list: authenticate it or stop it sending as you
fail / fail — unknown source, big countSpoofing at volume, or a major forwarderClassify via auth_results/reason; spoofer rows are your p=reject business case, not a fire
fail / fail — unknown, count of 2Background noise of the open internetLog it in the census; spend your worry by volume

Cadence and silence — calibrated expectations

  • Spec: generators SHOULD report at least every 24 h (opt-outs exist); Google: “usually sent once a day”; Microsoft: typically daily, previous day's data, likely gzipped. The expected attachment is .xml.gz or plain XML — a .zip is outside the spec's documented forms, so treat unexpected archives with ordinary sender-verification suspicion.
  • No report ≠ no mail, and ≠ failure: non-participating receiver, zero observed traffic, or a report whose delivery itself failed (the spec permits discarded undeliverables). Judge by the reporters you do hear from, over weeks.
  • Tools honesty: parsers/visualisers exist as a category (the spec itself anticipates third-party report services, and Google recommends a dedicated rua mailbox or service as volume grows). We name categories, not champions: any tool you adopt gets read access to your sending census — choose like it matters, because it does.
  • Forensic reports (ruf): the unicorn. Far fewer receivers send them, privacy limits their content, and several major receivers don't send them at all. Design your process around aggregates; treat any forensic arrival as a bonus.

Traps

TrapReality
Panicking at fail rows while p=noneUnder monitor mode, disposition none on failures is the design: you asked for a census before enforcement. The fails ARE the product.
Reading rows as messagesEach row is a count-weighted aggregate. Sort by count or be misled by a row of 2 out-arguing a row of 20,000.
Every unknown IP = attackerForwarders, lists, and your own shadow IT fill this bucket. auth_results + reason classify; adrenaline doesn't.
Hand-counting XML foreverFine for week one (and genuinely instructive), unsustainable after. Move to a parser when the census is a habit — that's the spec's own anticipated workflow.

FAQ

Reports show a domain in policy_published I don't recognize as mine.

Subdomains inherit/report under your organisational domain's policy — a subdomain's traffic legitimately appears in your reports. Trace it via identifiers before suspecting a mix-up.

Can reports tell me WHO read my mail / message subjects?

No — aggregates are deliberately content-free: counts, sources, verdicts. That privacy shape is why receivers are willing to send them at all.

When do reports let me climb to p=reject?

When pass 5's math says your legitimate volume aligns (with the fix list empty) across a representative window — the rollout guide turns that into staged gates. Reports are the evidence; the climb is the decision.

Sources

RFC 9990 — DMARC Aggregate Reporting (format, schema incl. the two disposition enums, example report) · RFC 9989 — DMARC (§5.3.8 report cadence SHOULD, enforcement workflow) · RFC 7489 — legacy dialect differences (pct, optional selector) · Google — “About DMARC reports” (cadence, mailbox/service recommendation) · Microsoft Learn — DMARC reporting (daily cadence, carried from this site's earlier verified research).

Verification note: schema facts were verified against RFC 9990's text and schema on 20 October 2026. Yahoo's report-sending specifics have no first-party documentation we could verify, so none are claimed; named-tool endorsements are deliberately absent (category only).