To read a DMARC report, open the compressed XML attachment, look at policy_published to confirm which policy receivers applied, then go through every record block: the source_ip and count tell you who sent how much, policy_evaluated gives the DMARC verdict, and auth_results shows the raw SPF and DKIM results underneath it. A row that passes SPF or DKIM but fails DMARC has an alignment problem. A row that fails everything is either a forgotten sender or someone spoofing you. This guide walks through a real report field by field, shows the seven patterns you will see most often, and explains what to do about each one.
What a DMARC report actually is
Every mail receiver that supports DMARC (Google, Microsoft 365, Yahoo, Mail.ru, Fastmail, Zoho and many others) counts the messages it sees claiming your domain in the From header. Once per UTC day it packs those counts into an XML file, compresses it, and emails it to the address in the rua= tag of your DMARC record. These are aggregate reports, sometimes called RUA reports. They contain no message content, only counts per sending IP with the authentication results for each.
Per RFC 9990, published by the IETF in May 2026 as the Standards Track specification for aggregate reporting, the reporting period is typically a single UTC day starting at 0000 UTC, the XML must be gzip compressed in normal circumstances, and each report covers exactly one policy domain. Reports arrive whether your policy is p=none, p=quarantine or p=reject. They are the only feedback loop DMARC gives you, and reading them is the difference between enforcing safely and blocking your own invoices.
Of the top 1.8 million domains, 937,931 (52.1%) publish DMARC, over 553,000 collect aggregate reports, and 411,935 enforce. Only around 9% do both. Source: EasyDMARC, 2026 DMARC Adoption Report, 30 March 2026.
The gap in that chart is the problem this article solves. EasyDMARC's March 2026 study of the top 1.8 million domains found that only about 9% of domains combine an enforcement policy with reporting. A separate DMARCguard scan of 5,499,028 Tranco domains on 27 February 2026 found that 776,918 of the 1,670,975 DMARC domains (46.5%) have no rua= tag at all. Most domain owners who do receive reports never open them, because raw XML looks hostile. It is not. There are only three sections.
Step 1: open the attachment
A genuine report email has a subject like Report Domain: example.com Submitter: google.com Report-ID: 1234567890 and a single attachment. RFC 9990 section 3.5.2 fixes the filename format as receiver!policy-domain!begin-timestamp!end-timestamp.xml.gz, for example google.com!example.com!1757462400!1757548799.xml.gz. The two numbers are Unix timestamps for the start and end of the reporting window, so you can tell the covered day before you open anything.
Decompress it on the command line:
gunzip google.com!example.com!1757462400!1757548799.xml.gz
Some senders, including Microsoft, use zip instead of gzip. In that case use unzip on the file. To pretty-print the result for reading:
xmllint --format google.com!example.com!1757462400!1757548799.xml
Any text editor works too. What you will see is a single feedback element containing report_metadata, policy_published and one or more record blocks.
Step 2: read the three sections
Here is a report trimmed to one record, using the structure from RFC 9990 Appendix B:
<feedback xmlns="urn:ietf:params:xml:ns:dmarc-2.0">
<version>1.0</version>
<report_metadata>
<org_name>google.com</org_name>
<email>noreply-dmarc-support@google.com</email>
<report_id>1757548799.example.com</report_id>
<date_range>
<begin>1757462400</begin>
<end>1757548799</end>
</date_range>
</report_metadata>
<policy_published>
<domain>example.com</domain>
<adkim>r</adkim>
<aspf>r</aspf>
<p>quarantine</p>
<sp>none</sp>
</policy_published>
<record>
<row>
<source_ip>192.0.2.123</source_ip>
<count>123</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<envelope_from>example.com</envelope_from>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<domain>example.com</domain>
<result>pass</result>
<selector>abc123</selector>
</dkim>
<spf>
<domain>example.com</domain>
<result>fail</result>
</spf>
</auth_results>
</record>
</feedback>
Older reports use the namespace http://dmarc.org/dmarc-xml/0.1 from RFC 7489. The fields are the same, so read them the same way.
| Element | What it tells you | How to use it |
|---|---|---|
report_metadata / org_name
|
Which receiver generated the report | Group findings per receiver; Google and Microsoft together usually cover most of your volume |
date_range |
Unix timestamps of the covered window | Match spikes to campaigns or incidents on that day |
policy_published |
Your DMARC record as the receiver read it | If p differs from what you published, DNS had not propagated or you have two records |
source_ip |
The IP that delivered the message to the receiver | Reverse-lookup it; this is your sender inventory |
count |
Messages from that IP with identical results | Big counts are your real mail streams; tiny counts are noise or probes |
policy_evaluated / dkim and spf
|
Aligned DKIM and aligned SPF results | This is the DMARC verdict, after alignment, not the raw check |
disposition |
What the receiver did: none, quarantine, reject, or pass in RFC 9990 reports |
Tells you what actually happened to the mail |
reason |
Why a receiver overrode your policy |
mailing_list, trusted_forwarder, local_policy, policy_test_mode or other
|
header_from |
The domain in the visible From header | The domain that DMARC is protecting in this row |
envelope_from |
The RFC5321.MailFrom domain SPF checked | If it differs from header_from, SPF cannot align |
auth_results / dkim
|
Raw DKIM result, signing domain and selector | A pass here with a fail in policy_evaluated means the d= domain is not yours |
auth_results / spf
|
Raw SPF result and the domain checked | A pass here with a fail above means the envelope domain is not aligned |
The one distinction that trips up almost everyone: auth_results is the raw check, policy_evaluated is the check after alignment. DMARC only counts SPF or DKIM as passing if the authenticated domain matches your header_from domain. A marketing platform signing with its own domain will show DKIM pass in auth_results and DKIM fail in policy_evaluated. That is not a broken signature. It is an unaligned one.
Step 3: classify every row
Once the fields make sense, reading a report is pattern matching. These seven patterns cover nearly every row you will encounter.
| # | What the row shows | Most likely cause | What to do |
|---|---|---|---|
| 1 | SPF aligned pass, DKIM aligned pass, high count | Your own mail server or a correctly configured service | Nothing. This is the target state |
| 2 | SPF aligned pass, DKIM fail or missing | A service you authorised in SPF that is not signing with your domain | Enable DKIM at that service and publish its CNAME or TXT selector |
| 3 | SPF fail, DKIM aligned pass, envelope_from is another domain |
Forwarding, a mailing list, or a platform using its own bounce domain | Normal. DMARC still passes. Do not add the forwarder to SPF |
| 4 | Raw DKIM pass with signing domain not yours, aligned DKIM fail | Third-party sender signing with its own default key | Ask the vendor for a custom signing domain (a CNAME under yours) |
| 5 | Raw SPF pass for envelope_from, aligned SPF fail |
Vendor uses its own return-path domain | Set a custom return-path or bounce domain at the vendor, or rely on DKIM |
| 6 | Everything fails, source_ip belongs to a cloud or hosting range you do not recognise, count in the hundreds or thousands |
Spoofing or a phishing campaign using your domain | Leave it failing. Move toward p=reject so receivers drop it |
| 7 | Everything fails, low count, reason says mailing_list or trusted_forwarder
|
Receiver exempted the message from your policy | Informational. Nothing to fix on your side |
For a row you cannot place, reverse-lookup the IP with dig -x 192.0.2.123 +short or host 192.0.2.123, then check the SPF include for your suspected vendor. A PTR record ending in outbound.protection.outlook.com, google.com, amazonses.com, sendgrid.net or mailgun.org usually points straight at the responsible service.
Reading reports at volume
A domain with a few senders produces a handful of files a day, and hand reading works. Above that, use an analyser. A typical analyser ingests the rua= mailbox, decompresses each file, and rolls the rows up by source, so you see "Microsoft 365: 12,400 messages, 100% aligned" rather than forty XML files. Free options exist, and FortifyNet's own scan will tell you whether a rua= tag is even present and syntactically correct.
Two habits matter more than the tool. First, watch policy_published after every DNS change: receivers cache DMARC records, so a report covering the day you changed p= may show the old policy, and RFC 9990 section 3.3 explicitly allows a mix. Second, keep reading after you reach p=reject. New SaaS tools, rotated DKIM keys and edited SPF records all show up as a new pattern 2, 4 or 5 row the next day, long before your customers notice bounces.
What RFC 9990 changed for readers
The May 2026 standard, which obsoletes the reporting parts of RFC 7489 from 2015, matters if you parse reports yourself or evaluate a vendor.
| Field or rule | RFC 7489 (2015) | RFC 9990 (2026) |
|---|---|---|
| XML namespace | http://dmarc.org/dmarc-xml/0.1 |
urn:ietf:params:xml:ns:dmarc-2.0 |
DKIM selector
|
Optional | Required whenever a DKIM result is reported |
disposition values |
none, quarantine, reject | Adds pass
|
| Override reasons | forwarded, sampled_out, trusted_forwarder, mailing_list, local_policy, other | local_policy, mailing_list, other, policy_test_mode, trusted_forwarder |
discovery_method |
Not present | Optional: psl or treewalk
|
generator |
Not present | Optional name and version of the reporting software |
envelope_from |
Optional | Optional, may be empty for bounces with a null sender |
| Unknown elements | Undefined | Must be ignored, not treated as errors |
Every major receiver still sends legacy-shaped reports in September 2026, and will for a long time. Read both formats the same way and never discard a report because it has a field you do not recognise.
FAQ
Why do I get DMARC reports for mail I never sent?
Because someone else is sending it. Aggregate reports cover every message that claimed your domain in the From header, including spoofed ones. A row with a high count, an unknown IP and failures on both SPF and DKIM is exactly the spoofing you set up DMARC to catch.
How many reports should I expect per day?
One per reporting receiver per day per domain, so typically 3 to 15 files for a small business. RFC 9990 sets the period to a single UTC day. Very large receivers may split a day into several files.
What does disposition none mean when I have p=quarantine?
Either the message passed DMARC, so no policy applied, or the receiver overrode your policy and the reason element says why. Check policy_evaluated: if both aligned results are pass, none is the correct disposition.
Do DMARC reports contain email content or personal data?
Aggregate reports contain sending IPs, counts and authentication results, nothing from the message body or headers beyond the From domain. Failure reports (RUF, defined in RFC 9991) can contain headers and are rarely sent by major providers.
Can I stop receiving reports?
Remove the rua= tag and receivers stop within a day. You lose all visibility into spoofing and misconfiguration when you do, so pointing rua= at an analyser is the better option.
Check whether your reports are even arriving
Before you spend an evening with XML, it helps to know whether your DMARC record asks for reports at all, whether the rua= address is authorised, and whether SPF and DKIM are set up on the senders you already know about. FortifyNet's free scan reads your DMARC, SPF and DKIM records and flags what is missing in about 60 seconds, with no signup. Run a free scan at FortifyNet.
Related guides: DMARC Monitoring: How to Reach p=reject Safely · How to Set Up DMARC, Step by Step · What Is DKIM? · SPF Record Checker
Originally published at fortifynet.com/blog/how-to-read-dmarc-reports. I'm the founder of FortifyNet, a website security scanner; this article comes from our blog, so factor in that founder bias when you read any tool recommendations here.
Top comments (0)