Publishing a DMARC policy is the single most effective thing you can do to stop someone from spoofing your domain. The record lives in DNS and tells every receiving mail server on the planet, Gmail, Microsoft 365, Yahoo, iCloud, how to treat messages that claim to be from you but fail SPF and DKIM.
But DMARC is not only about enforcement. It is also a feedback loop. When you build your record with the DMARC Generator, you include a rua tag pointing at an inbox that should receive aggregate reports, something like v=DMARC1; p=none; rua=mailto:[email protected];. Once that record is live, every receiving server that handles your mail starts sending you a daily summary of what it saw.
That is where the trouble starts. For a mid-sized company, "a daily summary from every receiver" means hundreds of emails a day, each carrying a compressed attachment (.xml.gz or .zip). Inside is dense, machine-formatted XML listing thousands of sending IPs, alignment results, and dispositions. Nobody reads that by hand and lives to tell about it. The DMARC Report Parser exists to turn that pile of XML into something you can actually act on.
What a raw DMARC report actually looks like
Every aggregate report follows the same official schema. Here is a trimmed-down example so you can see the shape of it:
<feedback>
<report_metadata>
<org_name>google.com</org_name>
<email>[email protected]</email>
<report_id>1234567890123456789</report_id>
<date_range>
<begin>1774137600</begin>
<end>1774224000</end>
</date_range>
</report_metadata>
<policy_published>
<domain>yourcompany.com</domain>
<p>none</p>
<sp>none</sp>
<pct>100</pct>
</policy_published>
<record>
<row>
<source_ip>198.51.100.42</source_ip>
<count>1450</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>yourcompany.com</header_from>
</identifiers>
<auth_results>
<dkim>
<domain>yourcompany.com</domain>
<result>fail</result>
</dkim>
<spf>
<domain>yourcompany.com</domain>
<result>pass</result>
</spf>
</auth_results>
</record>
</feedback>
That is one <record>. A real report has hundreds of them, and you get dozens of reports a day. Assessing by eye whether your legitimate mail is authenticating correctly is simply not realistic at that volume.
How the parser handles it
The parser does all the work in your browser. Paste raw XML, or drag and drop .xml, .xml.gz, or .zip archives straight in. From there it extracts and calculates a few things:
- Reporting organization and date range — who sent the report (Google, Microsoft, Comcast) and the exact window it covers, converting the Unix epoch timestamps into readable dates.
- Published policy — the policy that was actually enforced during that period, whether
p=none,p=quarantine, orp=reject. - Source IP aggregation — messages grouped by originating IP, with total volume and geographic origin.
- SPF and DKIM alignment — and this is the part people miss. Raw SPF or DKIM passing is not the same as DMARC alignment. A message can pass SPF on a technicality while the envelope-from domain fails to match the visible Header From, in which case DMARC alignment still fails. The parser separates the two.
- Disposition — what the receiver actually did: delivered, quarantined, or rejected.
Here is what a parsed summary looks like once the noise is stripped out:
| Sending Source | Count | SPF | DKIM | DMARC Align | Disposition |
|---|---|---|---|---|---|
| Google (209.85.220.41) | 4,210 | PASS | PASS | PASS | Delivered (None) |
| Mailchimp (198.2.178.52) | 850 | PASS | FAIL | FAIL | Delivered (missing DKIM key) |
| Rogue botnet (192.0.2.14) | 12,400 | FAIL | FAIL | FAIL | Quarantined/Rejected |
One glance tells the whole story. Google is clean. Mailchimp is a real service you use but its DKIM key was never published, so it is failing alignment and needs fixing. And that botnet is an outright spoofer that should be blocked.
How often reports arrive
Aggregate reports are periodic, not real-time. Each receiver batches a full day of activity for your domain and sends one XML summary, so the cadence is roughly once per day, per receiver. You can request a different interval with the ri tag (the default is ri=86400, i.e. 86,400 seconds = 24 hours), but it is only a hint: virtually every large mailbox provider sends daily regardless of what you ask for. With Google, Microsoft, Yahoo, and a long tail of smaller receivers all reporting, a busy domain sees dozens of aggregate emails a day.
Failure reports run on a completely different clock. They are generated per failing message, in near real-time, the moment a message fails. In theory that gives you instant forensic detail; in practice you will rarely see one, because most major providers stopped sending them years ago.
Aggregate vs failure (forensic) reports
DMARC actually defines two report types, requested by two different tags:
Aggregate report (rua) |
Failure / forensic report (ruf) |
|
|---|---|---|
| Requested by | rua=mailto:… |
ruf=mailto:… |
| Frequency | Periodic — about once a day per receiver | Per failing message, near real-time |
| Format | XML summary, usually gzip or zip | AFRF — a redacted copy of the individual message |
| Contains | Per-source-IP counts, disposition, SPF/DKIM alignment results, your published policy — no message content or recipients | Message-level detail: headers (From/To/Subject, often redacted), the failing authentication result, source IP |
| Good for | Seeing who sends as your domain and whether they authenticate, over time | Investigating why one specific message failed |
| Availability | Sent widely and reliably | Rare — Google, Microsoft, and most others do not send them, for privacy reasons |
The practical upshot: aggregate reports are the workhorse. They are what you actually receive, and they are enough to get a domain safely to p=reject; the parser is built around them. Failure reports would be handy for pinpointing a single broken message, but because a real copy of someone's email is a privacy minefield, the big receivers largely abandoned them, so do not build your process around data you probably will not get.
The fo tag: tuning failure reports
If you do set a ruf address, the fo (failure reporting options) tag controls when a failure report is generated. It has no effect on aggregate reports:
fo=0(the default) — report only if both SPF and DKIM fail to produce an aligned pass. The narrowest setting; you will get almost nothing.fo=1(recommended) — report if either SPF or DKIM fails alignment, even when DMARC overall still passes because the other aligned. This surfaces one mechanism silently breaking before it becomes a delivery problem.fo=d— report on a DKIM signature failure specifically.fo=s— report on an SPF failure specifically.
You can combine them with colons, e.g. fo=1:d:s. The DMARC Generator sets fo=1 by default once you add a ruf address. Just remember that fo only decides whether a failure report would be sent; it cannot force a receiver that has disabled forensic reporting to send one.
The three phases to full enforcement
The endpoint you want is p=reject, which tells the world to throw away any unauthorized mail pretending to come from you. But jumping straight there without reading your reports is reckless. You will almost certainly block legitimate corporate mail, the marketing platform, the HR system, the billing service, and only find out when someone complains. The reports are how you avoid that. Work through them in order:
Monitoring (
p=none). Publish a record withp=noneso receivers report failures without blocking anything. Collect reports for two to four weeks and feed them to the parser. It shows you every service sending on your behalf, the legitimate ones (Salesforce, Zendesk) alongside the spoofers.Remediation and alignment. Now fix what the breakdown surfaced. Add missing IPs or
include:directives to your record, which you can check against the SPF record lookup; if that record is bumping against the 10-lookup limit, the SPF Flattening tool helps. Publish DKIM keys for third-party platforms like Mailchimp and HubSpot, and confirm them with the DKIM record lookup. Finally, verify the Header From domain aligns with your SPF and DKIM signing domains.Strict enforcement (
p=quarantine→p=reject). Once the parser shows 100% of your legitimate sources passing DMARC, move top=quarantineso unauthenticated mail goes to spam. When that holds steady, switch top=rejectand block it outright. At that point, spoofing your exact domain stops working. You can confirm the live policy any time with the DMARC record lookup.
A real scenario: the phishing run nobody saw
A regional credit union had recently published a p=none record. Its IT team was getting dozens of .xml.gz files a day from Microsoft and Google and, understandably, never had time to decompress and read them.
One afternoon the IT director dropped a week's worth of archives into the parser. The result was ugly. Legitimate employee mail through Google Workspace was passing fine, but a suspicious IP range in another country had pushed more than 45,000 messages over the weekend using the union's exact domain, every one of them failing SPF and DKIM. Because the domain was still on p=none, all 45,000 phishing emails aimed at the union's own customers had landed in inboxes.
With clear proof that no legitimate tool was failing, the director moved the record to p=reject. The next day's scan told the rest: the attacker tried another 20,000 messages, and every one was rejected at the boundary.
The takeaway
DMARC reports are only useful if you can read them, and at real volume you cannot read them without help. Decompressing, parsing, grouping by source, and separating raw authentication from true alignment is exactly the tedious work the parser takes off your hands. Start on p=none, spend a couple of weeks reading what comes back, fix your legitimate senders, and then close the door with p=reject. That is the whole game.