dmarc

How to Read a DMARC Aggregate (RUA) Report: The XML Decoded Field by Field

A DMARC aggregate report is a daily XML file listing every IP that sent mail as your domain and whether each passed DMARC. This guide walks a real annotated sample field by field: report_metadata, policy_published, and record rows, including the difference between raw and aligned results. Then it gives three triage patterns so you can tell legitimate-but-failing senders from spoofing and know the exact action for each, no paid dashboard required.

Jul 3, 20266 min read

A DMARC aggregate report (the "rua" report) is an XML file that a mailbox provider like Google or Microsoft sends you once a day, listing every IP address that sent mail using your domain in the From header, and whether each one passed DMARC. To read one, you check three blocks in order: who sent the report and when, what policy they evaluated against, and then each record row showing a source IP, a message count, and the pass or fail result for SPF and DKIM alignment. Once you can read the raw XML, you can tell legitimate-but-misconfigured senders apart from outright spoofing without paying for a dashboard.

Reads public DNS only. Nothing is stored unless you save the domain to an account.

The three parts of every aggregate report

Every aggregate report follows the schema defined in RFC 7489 Appendix C. The root element is <feedback>, and inside it you always get three kinds of blocks:

  • <report_metadata> - who generated the report and the time window it covers.
  • <policy_published> - the DMARC record the reporter found at your domain when they evaluated the mail.
  • One or more <record> elements - the actual sending sources, grouped by IP and result.

Reports are per-day and per-reporter, so a single domain with real traffic will get several files daily. They describe mail that already happened. They are a rear-view mirror, not a live feed, which is why triage is about spotting patterns across sources rather than reacting to one message.

report_metadata: who is telling you this

<report_metadata>
 <org_name>google.com</org_name>
 <email>noreply-dmarc-support@google.com</email>
 <report_id>1234567890123456789</report_id>
 <date_range>
 <begin>1719964800</begin>
 <end>1720051200</end>
 </date_range>
</report_metadata>

org_name is the reporter, here Google. The date_range values are Unix timestamps in UTC. Convert 1719964800 and you get the 24-hour window this file covers. Keep the report_id because if you ever open a support ticket with the reporter, that is the reference they need. This block never tells you anything is wrong. It only frames the rest.

policy_published: the identity that carries DMARC

<policy_published>
 <domain>yourdomain.com</domain>
 <adkim>r</adkim>
 <aspf>r</aspf>
 <p>none</p>
 <sp>none</sp>
 <pct>100</pct>
</policy_published>

This is the reporter's copy of your published DMARC record. It matters because DMARC does not care whether SPF or DKIM passed on their own. It cares whether either one passed and aligned with the domain in the visible From header. Alignment is the whole game.

  • adkim and aspf set alignment mode. r is relaxed (the organizational domain must match, so mail.yourdomain.com aligns with yourdomain.com). s is strict (an exact match is required).
  • p is your policy for the organizational domain: none, quarantine, or reject.
  • sp is the policy for subdomains. If it is missing, subdomains inherit p.
  • pct is the percentage of failing mail the policy is applied to.

If this block does not match the record you think you published, someone changed your DNS or you are looking at a subdomain you forgot about. For the difference between the three policy levels and when to move up, see DMARC policy: none vs quarantine vs reject.

record: reading a single source row

This is where you spend your time. Each <record> has three children: row, identifiers, and auth_results.

<record>
 <row>
 <source_ip>203.0.113.25</source_ip>
 <count>418</count>
 <policy_evaluated>
 <disposition>none</disposition>
 <dkim>pass</dkim>
 <spf>pass</spf>
 </policy_evaluated>
 </row>
 <identifiers>
 <header_from>yourdomain.com</header_from>
 </identifiers>
 <auth_results>
 <spf>
 <domain>yourdomain.com</domain>
 <result>pass</result>
 </spf>
 <dkim>
 <domain>yourdomain.com</domain>
 <selector>s1</selector>
 <result>pass</result>
 </dkim>
 </auth_results>
</record>

row and policy_evaluated

source_ip is the IP that sent the mail. count is how many messages this exact result applied to, so a spoofing burst shows up as one row with a large count. policy_evaluated is the reporter's verdict:

  • disposition is what actually happened to the mail: none, quarantine, or reject.
  • The dkim and spf values inside policy_evaluated are the aligned results, not the raw ones. This is the single most misread field in the whole format. A message can have DKIM pass in auth_results but show fail here because the signing domain did not align with the From domain.

DMARC passes when at least one of these two aligned checks is pass. Both can fail and DMARC still fails.

identifiers and auth_results

header_from is the domain a human sees in their mail client. This is the identity DMARC protects. auth_results shows the raw SPF and DKIM outcomes before alignment: the domain SPF authenticated, and the domain plus selector that DKIM signed with. Comparing auth_results domains against header_from is how you diagnose alignment failures by hand. If DKIM signed for sendgrid.net but header_from is yourdomain.com, raw DKIM passed and aligned DKIM failed. That is the pattern behind most DKIM alignment failures.

Triage: three patterns and the exact action for each

Once you can read a row, sort every source into one of three buckets.

Pattern 1: aligned pass, do nothing

policy_evaluated shows dkim or spf as pass, and the auth_results domain is your own domain or a subdomain. This is your legitimate mail flowing correctly. Note the IP so you recognize it, and move on.

Pattern 2: legitimate but failing, fix it

The source_ip belongs to a service you actually use (your CRM, your invoicing tool, a marketing platform), but policy_evaluated shows both dkim and spf as fail. Look at auth_results. If SPF passed for the vendor's own domain but not yours, the vendor is not in your SPF record or is sending with a return-path you do not authorize. If DKIM passed for the vendor domain, you need to set up domain-aligned DKIM signing with that vendor. Fix these before you raise your policy, or you will block your own mail. The setup steps are in how to set up DKIM and how to set up DMARC.

Pattern 3: unknown source, failing, likely spoofing

The source_ip is one you do not recognize, it geolocates somewhere you have no infrastructure, header_from is your domain, and both aligned checks fail. High count values from a single strange IP are the classic signature. This is the mail your DMARC policy exists to stop. As long as these sources cannot produce an aligned pass, moving your policy to p=quarantine and then p=reject shuts them out. This is exactly how DMARC blocks brand spoofing, covered in how to stop email spoofing of your domain.

From reading to acting

The goal of reading reports by hand is not to do it forever. It is to build a verified list of every legitimate sender, get each one to an aligned pass, and then confidently publish a record like v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com; adkim=s; aspf=s. Aggregate XML does not show message content or recipients, so it is safe to enforce on without privacy risk. If a row still confuses you, cross-check it against the raw headers of a real message using our guide to the Authentication-Results header, which shows the same pass and fail decisions from the receiving server's point of view.

Frequently asked questions

What is the difference between the SPF result in auth_results and in policy_evaluated?

The auth_results SPF result is the raw RFC 7208 outcome for whatever domain SPF authenticated, usually the return-path. The policy_evaluated SPF result is the aligned result: it is pass only if that raw pass also aligns with the From domain. DMARC uses the aligned one.

Why do I get so many aggregate reports?

Each mailbox provider sends one report per day for your domain, and you get a separate file from every provider that received your mail. A domain sending to Google, Microsoft, and Yahoo users will get at least three files daily, plus more from smaller receivers.

Can an aggregate report show me the actual email content?

No. Aggregate reports contain only IP addresses, counts, authentication results, and policy data. They never include subject lines, message bodies, or recipient addresses. Message-level detail lives in forensic (ruf) reports, which most large providers no longer send for privacy reasons.

Do I need a paid tool to read DMARC reports?

No. The XML is a documented, readable format, and for a small domain you can triage the files by hand using the three patterns above. Paid dashboards mainly save time by aggregating and geolocating sources at scale. Understanding the raw XML first means you can verify what any tool tells you.

Check your own domain

Run a free scan and get your grade with the exact records to fix.

Scan a domain

Related guides

How to Read a DMARC Aggregate (RUA) Report