How to Read a DMARC Aggregate (RUA) Report XML
VigilDog Team · October 16, 2026 · 6 min read
The day you publish a DMARC record with a rua= tag, mailbox providers start emailing you XML files, often zipped, often several a day. They look like noise, but each one is a precise census of who is sending mail as your domain and whether it passed authentication. Learning to read a DMARC aggregate report is the difference between guessing at your email security and knowing it.
What a DMARC aggregate report actually contains
A DMARC aggregate report, the one your rua= tag asks for, is a daily summary that a mailbox provider sends back to you. It is not a copy of anyone's email. It contains no subject lines and no message bodies, only counts: how many messages arrived from each sending IP claiming to be your domain, and how each one fared against SPF, DKIM, and the DMARC policy you published. Google, Yahoo, Microsoft, and most large receivers generate one per day.
That distinguishes it from a forensic (RUF) report, requested with ruf=, which contains redacted samples of individual failing messages. Forensic reports are rare in practice, most providers don't send them for privacy reasons, so the aggregate report is what you will actually work with. Think of it as a census: every source sending mail as you, tallied and scored against the authentication chain.
The XML structure, top to bottom
Reports usually arrive as a gzip or zip attachment with a name like google.com!yourdomain.com!1727308800!1727395200.xml.gz. Decompressed, every report has the same three-part shape, and the first two parts are just context before the data begins.
The <report_metadata> block names who sent the report, a unique report_id, and a date_range in Unix timestamps. The <policy_published> block echoes back the DMARC record the receiver actually observed, which is worth checking against what you think you published:
<policy_published> <domain>yourdomain.com</domain> <adkim>r</adkim><aspf>r</aspf> <p>none</p><sp>none</sp><pct>100</pct> </policy_published>
Here adkim and aspf are the alignment modes (r = relaxed, s = strict), p is your main policy, sp the subdomain policy, and pct the percentage of mail the policy is applied to during a rollout. After this header come one or more <record> blocks, the part that actually matters.
- report_metadata, the sender, report ID, and the day the report covers
- policy_published, the exact DMARC record the receiver saw for your domain
- record (one or more), the per-source rows with counts and results
Reading a single record: alignment is everything
Each <record> describes one sending source. A typical one looks like this:
<record> <row> <source_ip>203.0.113.10</source_ip> <count>42</count> <policy_evaluated> <disposition>none</disposition> <dkim>pass</dkim><spf>fail</spf> </policy_evaluated> </row> <identifiers><header_from>yourdomain.com</header_from></identifiers> <auth_results> <spf><domain>bounces.mailer.com</domain><result>pass</result></spf> <dkim><domain>yourdomain.com</domain><selector>s1</selector><result>pass</result></dkim> </auth_results> </record>
The <row> gives you the source_ip, the count of messages from it, and policy_evaluated, the receiver's verdict. disposition is what they did with the mail (none, quarantine, or reject), and the dkim/spf lines there are the DMARC-aligned results. identifiers.header_from is the From domain your recipients actually saw.
The trap is the difference between <auth_results> and policy_evaluated. auth_results shows the raw checks; policy_evaluated shows them after alignment. In the example, raw SPF passed for bounces.mailer.com, yet policy_evaluated spf is fail because that Return-Path domain doesn't align with the header From. DKIM, however, passed and was signed by d=yourdomain.com, so it aligns, and because DMARC passes when either aligned mechanism passes, this message is fine. Misreading that gap is the single most common DMARC mistake.
Turning rows into decisions
Aggregate a week of reports and group the records by sending source, mapping each IP back to a known service. Legitimate sources that fail alignment are your action items: an ESP you forgot to add to SPF, a DKIM selector you never published, or mailing-list forwarding that rewrites the Return-Path. Those are configuration gaps, and fixing SPF, DKIM, and DMARC is the work that closes them.
Unknown IPs that fail everything are either spoofing attempts or shadow IT, some SaaS tool sending as you that nobody documented. Neither should tempt you to loosen your policy. Once every legitimate source authenticates cleanly, you can raise enforcement, which is exactly the sequence in a DMARC p=reject rollout.
Reading raw XML by hand works for a handful of reports; at volume you want a parser or dashboard that de-duplicates sources over time. To confirm the policy the reports show is the one you actually intended, validate your live record with the DMARC checker before you trust a week of parsing built on top of it.
Where the reports stop helping
Aggregate reports are honest but slow. They arrive a day late, in a format built for machines, and they only describe mail that was actually sent. They will not warn you the moment a DKIM key is rotated out, an SPF record is edited past its ten-lookup limit, or a DMARC policy is quietly downgraded during an incident and never restored.
That is where continuous monitoring earns its place: checking the published records themselves, on a schedule, and alerting you before the next batch of XML confirms the damage. The reports tell you what happened yesterday; monitoring tells you what changed today.
