Understanding DMARC aggregate reports: what the XML is telling you
Publish a DMARC record with a rua address and within a day or two your inbox starts filling with attachments named things like google.com!yourdomain.com!1749600000!1749686400.xml.gz. These are aggregate reports, and they are the only honest picture you will ever get of who is sending mail as your domain. They are also nearly unreadable by hand, which is why most people receive them for months without ever opening one.
Why aggregate reports exist
DMARC has a built in feedback loop. When you publish a record like this:
v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com
every large receiver that gets mail claiming to be from your domain (Google, Microsoft, Yahoo, and many smaller operators) will send you a daily summary: which IP addresses sent that mail, how much of it, and whether each source passed SPF and DKIM with alignment.
This matters because you almost certainly do not know every system sending as your domain. The marketing platform someone signed up for two years ago, the CRM that sends quotes, the monitoring server that emails alerts through a relay. Aggregate reports surface all of it, including the forgeries. Without them, tightening your DMARC policy is guesswork, and the failure mode of guessing wrong is legitimate mail silently going to spam.
Why they arrive as zipped XML
The format is defined in RFC 7489: an XML document, compressed as gzip or zip, attached to an email. It was designed for machine consumption, with the assumption that domain owners would feed reports into tooling rather than read them. The compression is there because a busy domain can generate reports with thousands of records, and the XML is verbose.
In practice many domain owners are one person with a small domain and no pipeline, which is exactly the case the DMARC report viewer handles: drop the .xml, .gz, or .zip file in and it decompresses and parses the report in your browser. The file never leaves your machine, which is worth caring about since reports reveal your sending infrastructure.
The structure of a report
Every report has three layers.
Report metadata identifies who sent the report and the time window it covers, almost always a 24 hour UTC day:
<report_metadata>
<org_name>google.com</org_name>
<report_id>8163115399696594901</report_id>
<date_range><begin>1749600000</begin><end>1749686400</end></date_range>
</report_metadata>
Policy published echoes back the DMARC record the receiver saw when it evaluated your mail. If you recently changed your record, this tells you whether the change had propagated.
Records, the part that matters: one entry per unique combination of source IP and evaluation result.
<record>
<row>
<source_ip>209.85.220.41</source_ip>
<count>137</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>
<dkim><domain>yourdomain.com</domain><result>pass</result></dkim>
<spf><domain>bounce.someesp.com</domain><result>pass</result></spf>
</auth_results>
</record>
Note the apparent contradiction in that example: auth_results says SPF passed, but policy_evaluated says SPF failed. Both are correct, and the difference is alignment.
What alignment means
Raw SPF and DKIM each validate a domain, but not necessarily your domain. SPF validates the envelope sender; DKIM validates whatever domain is in the signature's d= tag. DMARC adds the requirement that the validated domain must match the domain in the visible From header. That match is alignment.
In the example above, SPF passed for bounce.someesp.com because the sending service uses its own bounce domain. That is a real pass, but it is not aligned with yourdomain.com, so it contributes nothing to DMARC. The DKIM signature, which carries d=yourdomain.com, is what made the message pass overall. DMARC needs only one aligned pass.
The aspf and adkim tags in your record control how strict the match is. Relaxed (the default) accepts an organizational domain match: mail.yourdomain.com aligns with yourdomain.com. Strict requires an exact match. Relaxed is the right choice for almost everyone; strict mostly exists for organizations that need to prevent one subdomain from sending as another.
Why legitimate mail fails
When you start reading reports, you will find real mail failing, and the causes are predictable.
Forwarding breaks SPF. When someone forwards mail from their old address to their new one, the forwarding server resends your message from its own IP, which is not in your SPF record. There is no fix on your side; this is exactly why DMARC accepts DKIM alone, since a signature survives forwarding as long as the message is unmodified.
Mailing lists break DKIM. Lists commonly add a subject prefix like [listname] or append a footer, which invalidates the signature over the original body. Many lists now work around this by rewriting the From header to their own domain, which is why list mail often arrives "via" the list domain.
A persistent few percent of failing volume from forwarders and lists is normal and not a reason to stay at p=none forever.
The rollout path
The policy progression is p=none, then quarantine, then reject, and the reports decide the pace.
- Start at
p=nonewith aruaaddress. Nothing changes for your mail; you are only collecting data. The DMARC generator produces a correct starting record. - Identify every legitimate source in the reports. For each one, get it aligned: add its IPs or include to your SPF record (the SPF generator helps keep the syntax and lookup count right) and, more importantly, set up DKIM signing with your domain in the
d=tag (keys via the DKIM generator). DKIM alignment is the durable one. - Move to
quarantinewhen reports are clean. Clean means every source you recognize passes with alignment, and what remains failing is either forwarding noise or mail you genuinely want stopped. You can ease in withpct=25to apply the policy to a fraction of failing mail. - Move to
rejectafter quarantine has run for a few weeks without complaints. Reject is the goal; it is the only policy that actually stops spoofing rather than routing it to spam folders.
There is no fixed timeline, but a month at each stage is a reasonable default for a small domain, longer for an organization with many sending systems. The reports are the safety mechanism: as long as you read them before each step, tightening the policy is a controlled change rather than a leap.