How to Read DMARC Reports (With Real Examples)
A practical walkthrough of reading DMARC aggregate XML reports. Real examples, field-by-field explanations, and how to spot spoofing.
You set up DMARC. You added a rua= address. Now your inbox is filling up with XML attachments from Google, Microsoft, Yahoo, and a dozen other senders, and you have no idea what to do with them.
This article is the practical walkthrough. We'll look at a real aggregate DMARC report, break down every field, and show you how to tell the difference between a legitimate sender that needs a config fix and someone actively spoofing your domain.
What a DMARC Report Actually Is
A DMARC aggregate report is an XML file that a receiving mail provider sends you once per day (by default). It summarizes every message the provider saw claiming to be from your domain over a 24-hour window. For each sending IP, the report tells you:
- How many messages came from that IP
- What SPF and DKIM results the provider got
- Whether those results aligned with your From domain
- What action the provider applied (none, quarantine, reject)
The important word is aggregate. You won't see individual email content, subject lines, or recipients. You see counts and results, grouped by source IP.
If you need detail on the difference between aggregate (RUA) and forensic (RUF) reports, read DMARC RUA and RUF explained.
A Real Aggregate Report, Unpacked
Here's a realistic (slightly trimmed) aggregate report from Google. We'll walk through it top to bottom.
<feedback>
<report_metadata>
<org_name>google.com</org_name>
<email>[email protected]</email>
<report_id>14892733891274</report_id>
<date_range>
<begin>1712016000</begin>
<end>1712102400</end>
</date_range>
</report_metadata>
<policy_published>
<domain>example.com</domain>
<adkim>r</adkim>
<aspf>r</aspf>
<p>quarantine</p>
<sp>quarantine</sp>
<pct>100</pct>
</policy_published>
<record>
<row>
<source_ip>209.85.220.41</source_ip>
<count>847</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<domain>example.com</domain>
<result>pass</result>
<selector>google</selector>
</dkim>
<spf>
<domain>example.com</domain>
<result>pass</result>
</spf>
</auth_results>
</record>
<record>
<row>
<source_ip>185.220.101.47</source_ip>
<count>312</count>
<policy_evaluated>
<disposition>quarantine</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<spf>
<domain>unknown-host.ru</domain>
<result>fail</result>
</spf>
</auth_results>
</record>
</feedback>
The report_metadata Block
This tells you who sent the report and when. org_name is the reporter (Google, in this case). The date_range is in Unix timestamp format — those values translate to a 24-hour window on April 2, 2026. If you're reading these by hand, keep a timestamp converter open.
report_id is a unique ID you can quote if you need to ask the reporter about a specific report.
The policy_published Block
This is a snapshot of your DMARC record as the reporter saw it during that window. This matters: if you changed your policy mid-day, you'll see the old and new policies reflected in different reports. Verify this matches what you actually intended. If it doesn't, someone changed your record, or you have multiple DMARC records and the reporter picked the wrong one.
Tags to notice:
adkim/aspf— alignment mode,r(relaxed) ors(strict)p— your policy (none,quarantine,reject)sp— subdomain policypct— percentage of messages the policy applies to
The record Blocks (The Important Part)
Each <record> is one sending IP, and this is where you actually do your work. Let's compare the two records above.
| Field | Record 1 (Good) | Record 2 (Suspicious) |
|---|---|---|
| **source_ip** | 209.85.220.41 (Google) | 185.220.101.47 (unknown) |
| **count** | 847 messages | 312 messages |
| **disposition** | none | quarantine |
| **DKIM evaluated** | pass | fail |
| **SPF evaluated** | pass | fail |
| **Auth domain** | example.com | unknown-host.ru |
Record 1 is healthy: an IP in Google's range sent 847 messages, both DKIM and SPF passed and aligned with example.com, and the disposition is none (delivered normally). This is your Google Workspace traffic working correctly.
Record 2 is a classic spoofing attempt: 312 messages from an IP that isn't yours, SPF and DKIM both failed, and the SPF check was performed against unknown-host.ru — a completely different domain. DMARC quarantined those messages. This is DMARC doing exactly what it's supposed to do.
Telling Legitimate Failures From Spoofing
Not every failure is an attack. The pattern you're looking for is this:
Legitimate sender with a config problem:
- Source IP belongs to a service you recognize (Mailchimp, SendGrid, Zendesk)
- SPF or DKIM passes, but alignment fails
- Volume is steady over time
- Fix: configure custom domain authentication with the service
Spoofing attempt:
- Source IP is unknown, often from a residential ISP or a hosting provider you don't use
- Both SPF and DKIM fail completely
- Volume can spike suddenly
- Action: nothing to fix on your end — your policy is protecting you
Forwarding:
- Source IP is a mail server you don't recognize but looks like a business or university
- SPF fails, DKIM passes
- Low volume, scattered IPs
- Fix: usually nothing — rely on DKIM to pass DMARC through forwarders
Whois the IP
When you see an unfamiliar source IP, do a quick whois lookup. If it resolves to a major email provider, a cloud host you use, or a service you recognize, it's probably legitimate. If it resolves to a random ASN in a country you don't do business in, you're probably looking at spoofing.
When to Use a DMARC Report Analyzer
Reading raw XML is fine for a small domain with three or four senders. It becomes impossible once you're getting 50+ reports a day from dozens of receivers. At that point, a DMARC report analyzer (sometimes called an aggregator) is worth the money — it parses every report, groups results by sender, resolves IPs to organizations, and shows you a dashboard.
But before you pay for one, make sure your DMARC record itself is correct. Use DMARC Creator to generate or validate your record, and run a quick check with the tool at the top of this page.
For the full context on DMARC reporting, see the DMARC reporting guide.
Turn Reports Into Action
Reading reports is only valuable if you actually change something. Every week, pull your reports and ask:
- Are all my legitimate senders passing DMARC?
- Is any new IP showing up that I don't recognize?
- Is spoofing volume going up or down?
- Can I move my policy closer to
p=reject?
If the answer to question 1 is yes and question 2 is no, you're ready to tighten enforcement. See our DMARC enforcement guide for how to do it safely.
Never miss a DMARC issue
Stop parsing XML by hand. Monitor your SPF, DKIM, DMARC and MX records daily and get alerts when something breaks.
Start Monitoring