iPGaze

Guide

How to Read DMARC Aggregate (RUA) Reports

Learn to read DMARC aggregate (RUA) reports: the XML structure, SPF and DKIM alignment, spotting spoofing, and moving safely from p=none to reject.

Publishing a DMARC record is only the start. The real value of DMARC comes from the reports it sends back, which show you exactly who is sending email using your domain, whether that mail passes authentication, and where impersonation is happening. Those reports are how you turn a passive "p=none" policy into a confident, enforcing one that blocks spoofers without breaking your own mail.

This guide explains the most useful of those reports: DMARC aggregate reports, requested with the "rua" tag. We will walk through how to start receiving them, how the underlying XML is structured, how to read SPF and DKIM alignment, how to tell legitimate senders apart from spoofing, and how to use what you learn to advance from monitoring to enforcement safely.

RUA vs RUF: two kinds of DMARC reports

DMARC defines two report types. Aggregate reports, requested with the "rua" tag, are periodic XML summaries of every source that sent mail claiming to be from your domain, grouped by sending IP and authentication result. They contain no message content or recipient addresses, which makes them safe to receive at scale and the backbone of any DMARC rollout.

Failure (or "forensic") reports, requested with the "ruf" tag, are sent in near real time for individual messages that fail DMARC and can include headers or partial content. Because they carry potentially sensitive data, most mailbox providers no longer send them for privacy reasons, so in practice aggregate reports are what you will actually work with. The rest of this guide focuses on RUA.

Turning on aggregate reporting

To start receiving aggregate reports, add an "rua" tag to your DMARC TXT record at "_dmarc.yourdomain.com". A monitoring-mode record looks like "v=DMARC1; p=none; rua=mailto:[email protected]". The "p=none" policy tells receivers not to act on failures yet, while "rua" tells them where to send the daily summaries. You can list multiple addresses separated by commas.

If you collect reports at a different domain than the one being reported on, that receiving domain must publish an authorization record (for example "yourdomain.com._report._dmarc.analyzer.example" with the value "v=DMARC1"), which report processors handle for you automatically. After publishing or editing the record, confirm the syntax and the "rua" address with the DMARC Check checker, and make sure the underlying DNS Lookup TXT record is resolving as expected before you wait for data to arrive.

Why reports are aggregate and daily

Aggregate reports are not sent per message. Each receiving organization collects DMARC results for your domain over a window, usually 24 hours, and sends one compressed XML file summarizing everything it saw. That is why you typically get one report per provider per day, arriving as a ".xml.gz" or ".zip" attachment, and why a freshly published record produces nothing for the first day or so.

Because the data is aggregated by source IP and result rather than by individual email, a single report line might represent thousands of messages collapsed into one row with a count. This keeps volume manageable and protects recipient privacy, but it also means you are reading statistics, not a message log. Expect to gather reports for two to four weeks before you have a reliable picture of all your senders.

Inside the XML: structure of a report

Each aggregate report is an XML document with a few predictable blocks. The "report_metadata" element identifies the reporting organization (the "org_name", such as Google or Microsoft), a contact address, a unique "report_id", and the "date_range" the report covers as Unix timestamps. The "policy_published" element echoes back the DMARC policy the receiver saw for your domain at the time, including the "p", "sp", "adkim" and "aspf" tags, which is handy for confirming your record was read correctly.

The heart of the report is a series of "record" elements. Each one has a "row" containing the "source_ip" that sent the mail and a "count" of how many messages came from it, a "policy_evaluated" block showing the DMARC disposition and the SPF and DKIM results after alignment, an "identifiers" block with the "header_from" domain, and an "auth_results" block listing the raw SPF and DKIM checks. Reading a report is mostly a matter of walking these records one source at a time.

Reading SPF and DKIM alignment

DMARC does not just ask whether SPF or DKIM passed; it asks whether they passed in alignment with the domain in the visible "From" header. In "auth_results" you will see the raw checks: an SPF entry with the envelope domain and a result, and a DKIM entry with the signing domain (the "d=" value), the selector, and a result. These tell you what authenticated, but not yet whether it counts for DMARC.

The "policy_evaluated" block is where alignment is applied. Its "dkim" and "spf" fields read "pass" only when the authenticated domain matches the "header_from" domain under your alignment mode (relaxed allows subdomains, strict requires an exact match). DMARC passes if either SPF or DKIM passes in alignment, so a row can show SPF failing while DKIM passes and still be delivered. When something fails, compare the "d=" or envelope domain against your From domain to see whether it is a true alignment problem or simply an unrelated sender. Re-verify your published records with the SPF Check and DKIM Check tools when alignment looks wrong.

Spotting legitimate sources vs spoofing

Walk through the source IPs in your reports and sort them into three buckets. The first is mail you recognize and that passes alignment, for example your Google Workspace or Microsoft 365 servers, your marketing platform, and your transactional provider; these need no action beyond confirming they keep passing. The second is mail you recognize but that is failing, such as a helpdesk or CRM you forgot to add to SPF or that is not signing with DKIM. These are configuration gaps to fix, not threats.

The third bucket is the one DMARC exists for: source IPs you do not recognize, failing both SPF and DKIM, sending from your exact "header_from" domain. That pattern, especially from scattered or overseas IP ranges, is the signature of spoofing and phishing. You can look up an unknown sending IP with the DNS Lookup reverse lookup to identify the owner. Do not be alarmed by forwarders either: legitimate mail relayed through mailing lists often breaks SPF but keeps DKIM aligned, which is exactly why DMARC accepts either one.

Using a report analyzer

Reading raw XML by hand works for a single small report but quickly becomes unmanageable once you receive dozens per week from different providers. A DMARC report analyzer ingests the ".gz" and ".zip" attachments, decodes them, and presents trends over time: which sources are passing, which are failing, message volumes per IP, and geographic breakdowns. This makes it far easier to spot a new spoofing campaign or a sender that suddenly started failing after a configuration change.

Many analyzers are free for low volumes, and some let you point your "rua" address straight at their inbox. Whether you use a hosted service or a self-hosted parser, the goal is the same: replace manual XML reading with a dashboard so you can make rollout decisions from evidence rather than guesswork. Whatever tool you choose, keep validating the published record itself with the DMARC Check checker so the data you collect is trustworthy.

Moving from p=none to quarantine and reject

The whole point of reading reports is to enforce DMARC without collateral damage. Stay at "p=none" until your reports show every legitimate source passing SPF or DKIM in alignment. Use that monitoring period to add missing senders to SPF, enable DKIM signing where it is absent, and confirm fixes with the SPF Check and DKIM Check tools. Only when the failing rows are exclusively spoofing or noise should you tighten the policy.

Advance gradually. Move to "p=quarantine", optionally with the "pct" tag to apply the policy to a fraction of mail (for example "pct=25") and ramp up as the reports stay clean. Once quarantine runs without trapping legitimate mail, move to "p=reject" for full protection, and use the "sp" tag to set a separate policy for subdomains. Keep monitoring the reports after each change, because new senders are added over time, and re-check the record with the DMARC Check tool at every step to confirm the change propagated.

Tools mentioned in this guide