Email and DNS
How to read DMARC aggregate reports
On this page
A DMARC aggregate report is a daily summary, in XML, from one mail provider about all the mail it received that claimed to come from your domain. Each <record> is one sending server: its IP address, how many messages it sent, and whether those messages passed DMARC. The two lines that matter most sit inside <policy_evaluated>: if either <dkim> or <spf> says pass, that mail passed DMARC.
The rest of the report explains why. This guide goes through a real-shaped report a section at a time, shows the one comparison that catches most problems, and ends with how to prove a fix worked. If you haven’t published a DMARC record with a rua address yet, start with how to set up DMARC and come back when the reports arrive.
What arrives and how to open it
Reports come as email attachments to the address in your rua tag. The standard says the attachment is an XML file, usually gzipped, with a file name built from four parts separated by !:
reporter.example!example.co.uk!1793750400!1793836799.xml.gz
That reads: the reporting organisation’s domain (reporter.example here), your domain, then the start and end of the reporting period as Unix timestamps (seconds since 1 January 1970, in UTC). Those two numbers are 00:00:00 and 23:59:59 UTC on 4 November 2026. Some file names carry an extra unique ID before the extension, and you may also get .zip files; they open the same way.
To read one by hand, save the attachment (not the email), unzip it, and open the .xml file in a text editor or a browser. To read a few weeks of them at once, drop the attachments straight into the DMARC report analyser. It unpacks .gz and .zip files in your browser, adds up every record and groups them by sending IP address; nothing is uploaded.
Reports contain no message content and no personal email addresses. The standard is explicit about this: they hold domain-level authentication results, IP addresses of sending servers, and counts.
The three parts of a report
Every aggregate report has the same skeleton under a <feedback> root:
| Section | What it tells you |
|---|---|
report_metadata | Who sent the report (org_name, email), a unique report_id, and the date_range as Unix timestamps. An error element here means the reporter had trouble with your DMARC record. |
policy_published | Your DMARC record as the reporter saw it: domain, p, and optionally sp, np, adkim, aspf, fo and testing. Check it matches what you think you published. |
record (one or more) | One per combination of sending IP, result and domains. This is where the work is. |
Each record has three parts of its own:
row:source_ip(the server that connected),count(messages), andpolicy_evaluated, which holds the DMARC verdict:disposition(what the receiver did) and the aligneddkimandspfresults, each simplypassorfail.identifiers:header_from, the domain in the From address your recipients see. Newer reports may also showenvelope_from(the bounce address domain SPF checked) andenvelope_to.auth_results: the raw SPF and DKIM results, with the domain each one was checked against. For DKIM you also get theselector, which often tells you which service signed the message.
Read one record: a worked example
Here is a trimmed report for example.co.uk, which has p=none and sends from Microsoft 365 plus a newsletter service. The IP addresses are documentation addresses; yours will be real ones.
<policy_published>
<domain>example.co.uk</domain>
<p>none</p>
</policy_published>
<record>
<row>
<source_ip>198.51.100.24</source_ip>
<count>412</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers><header_from>example.co.uk</header_from></identifiers>
<auth_results>
<dkim><domain>example.co.uk</domain><selector>selector1</selector><result>pass</result></dkim>
<spf><domain>example.co.uk</domain><result>pass</result></spf>
</auth_results>
</record>
<record>
<row>
<source_ip>203.0.113.50</source_ip>
<count>37</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers><header_from>example.co.uk</header_from></identifiers>
<auth_results>
<dkim><domain>mailer.example.net</domain><selector>s1</selector><result>pass</result></dkim>
<spf><domain>bounces.mailer.example.net</domain><result>pass</result></spf>
</auth_results>
</record>
<record>
<row>
<source_ip>192.0.2.77</source_ip>
<count>3</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers><header_from>example.co.uk</header_from></identifiers>
<auth_results>
<spf><domain>example.co.uk</domain><result>fail</result></spf>
</auth_results>
</record>
Read each record in the same order: count, verdict, then the domains.
- 198.51.100.24, 412 messages. Both aligned results pass, and both raw results are for
example.co.uk. The selectorselector1is the name Microsoft 365 uses for its first DKIM key. This is your own mail working properly. Nothing to do. - 203.0.113.50, 37 messages. The verdict is fail, fail. But look at
auth_results: DKIM and SPF both passed, just formailer.example.net, the newsletter service’s own domain. The mail is genuine; it isn’t aligned with your From address. Once you move top=quarantine, these 37 newsletters go to junk. - 192.0.2.77, 3 messages. No DKIM signature at all, and SPF failed for your domain because this IP isn’t in your SPF record. Either it’s someone spoofing you, or a device of yours (a scanner, a web server) sending direct. Look up who owns the IP before deciding: a reverse (PTR) lookup often names the host or provider.
In this report 412 of 452 messages passed, a pass rate of 91%. The 37 newsletter messages are the whole gap that matters.
Raw results versus DMARC results
Record two is the most common thing you will find, and the reason so many people misread these reports. The two sets of results answer different questions:
auth_resultssays whether a signature or SPF check was valid for some domain.policy_evaluatedsays whether a passing check was for your domain (the one inheader_from). This is alignment, and it’s the only thing DMARC cares about.
So when a raw result says pass and the verdict says fail, compare the domain in auth_results with header_from. If they differ, the service is authenticating as itself. With the default relaxed alignment, a subdomain of yours counts as a match (mail.example.co.uk aligns with example.co.uk); someone else’s domain never does. If your record sets adkim=s or aspf=s, the domains must match exactly.
DMARC needs only one of the two to pass and align. Mail that passes on DKIM alone is fine. Mail that passes on SPF alone works today, but forwarding breaks SPF, so it’s worth adding aligned DKIM for those senders too.
Decide what to do with each sender
Work down your sending sources, largest count first, and put each in one of four groups.
| What you see | What it means | What to do |
|---|---|---|
Aligned pass on DKIM, SPF or both | Your mail, set up properly | Nothing |
Raw pass for another domain, verdict fail | A real service sending as you without alignment | Set up DKIM with your own domain in that service (often called domain authentication or a custom sending domain) |
Your own DKIM domain with result fail | Your signature broke in transit, usually a forwarder or mailing list editing the message | Usually nothing, if it’s a small share of your mail |
No DKIM, SPF fail or softfail, unknown IP | Spoofing, or a sender you haven’t found yet | Identify the IP. If it’s yours, route it through your mail provider; if not, this is what enforcement blocks |
Two other things are worth a glance. A reason element inside policy_evaluated means the receiver didn’t apply your policy as published, with a type such as mailing_list, trusted_forwarder or local_policy. And the disposition shows what was actually done: under p=none it is none even for failing mail, which is exactly why monitoring is safe.
Collect at least two to four weeks of reports before tightening the policy. Monthly invoices, payroll and statements only appear once a month, and a week of reports can miss them completely.
Check your fix worked
Say you’ve added the newsletter service’s DKIM records for example.co.uk. First check the records are published: the email DNS checker shows your SPF and DMARC records and tests DKIM selectors. Then send a test newsletter to a Gmail or Outlook.com address, open the message source and find the Authentication-Results header. You want to see dkim=pass next to your own domain (shown as header.d=example.co.uk or header.i=@example.co.uk, not the service’s domain) and dmarc=pass.
The reports confirm it a day or two later. In the next reports, the record for 203.0.113.50 should show:
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>fail</spf>
</policy_evaluated>
...
<dkim><domain>example.co.uk</domain><selector>s1</selector><result>pass</result></dkim>
SPF still fails alignment, because the service still uses its own bounce domain, and that’s fine: DKIM carries it. When every sender you recognise passes like this for a couple of weeks, you’re ready for p=quarantine.
Common puzzles
The numbers don’t match what I sent
Each report covers only the mail that one provider received, and mail to providers that don’t send reports never appears. Treat the reports as a large sample, not a complete count.
The same IP appears in several records
A new record starts for each different combination of result and domains, so one server can show up two or three times: once for messages that passed and once for forwarded copies that didn’t, for example.
Reports list subdomains I don’t use
Mail with a From address on a subdomain that has no DMARC record of its own is reported under your main domain, with that subdomain in header_from. If it fails and you don’t send from it, it’s spoofing. The sp and np tags in your DMARC record control how strictly those are treated.
The same report arrived twice
Reporters are meant to reuse the same report_id and file name when they resend, so duplicates are easy to spot. The analyser skips duplicate reports for you; if you are adding up by hand, check the report_id.
Microsoft 365 mail fails with a .onmicrosoft.com DKIM domain
That is the provider’s default signing domain, so it doesn’t align. Turn on DKIM for your own domain; enabling DKIM in Microsoft 365 covers the two CNAME records and the switch in Defender.