To read a DMARC RUA XML report, open its report_metadata, policy_published and record blocks and match each source IP to a sender you recognise. This guide walks through finding the file, decoding each block under RFC 9990, and deciding what each row means. The mistake worth avoiding: reading the DMARC-aligned dkim and spf values inside policy_evaluated as the raw authentication result, which actually sits separately in auth_results.
Contents
- What you need before reading a RUA report
- Step 1: locate and decompress the report file
- Step 2: read report_metadata and policy_published first
- Step 3: read each record row
- Step 4: decide what each row means for your domain
- How to check you have read the report correctly
- Common mistakes when reading RUA reports
- Keeping RUA reporting healthy over time
- Start reading your own reports today
- Frequently asked questions
What you need before reading a RUA report
You need a live DMARC record with a rua= address before any report can arrive. A monitoring-only record such as:
v=DMARC1; p=none; rua=mailto:dmarc@example.com
sets the policy the p= tag defines in RFC 9989, and NCSC uses a similar worked example. Build one first with the free DMARC generator if you have not published a record yet.
A policy of 'none' means this DMARC record won’t affect the delivery of your email, but it will provide you with reports on where your outbound email appears to be coming from. Source: NCSC
Expect the first reports from major mailbox providers once the UTC day the record goes live has ended, since reports usually cover one UTC day and are sent once a day, and only from receivers that saw mail from your domain that day. Reports usually arrive as one compressed XML attachment, .gz or .zip, per reporting organisation, not as plain text in the email body.
Save the attachment and open it in a text editor, a browser, or a report viewer rather than in your email client. This guide covers aggregate (RUA) reports only; forensic (RUF) reports use a different, non-XML format, covered in the RUA and RUF guide.
Locate the file
Find the .xml.gz or .zip attachment in your rua mailbox and decompress it.
Read the metadata
Check report_metadata and policy_published before any record row.
Check each record
Match source_ip, disposition and aligned dkim/spf to a sender you know.
Act on each row
Fix aligned failures, investigate unknown IPs, ignore nothing.
Step 1: locate and decompress the report file
You find the report in the mailbox named in your rua= tag, then decompress it before anything inside is readable.
- Search that mailbox for the attachment: the subject line usually names your domain and the reporting organisation, while the reporting period sits in the file name.
- Save it. RFC 9990 names the file
receiver!policy-domain!begin!end.xmlor.xml.gz, for examplemail.receiver.example!example.com!1735689600!1735776000.xml.gz(RFC 9990). - Decompress it: double-click a
.zipfile, or rungunzip 'filename.xml.gz'(unzip 'filename.zip'for a.zipfile) from a macOS or Linux terminal. Keep the single quotes around the real file name: bash and zsh treat its exclamation marks as history shortcuts, so the command fails without them. Microsoft's list of archive formats Windows opens natively does not include plain.gz, so on Windows drag a.gzfile straight into the free DMARC report viewer, which opens.gzfiles directly. - Open the resulting
.xmlfile in a text editor, or drag it into the free DMARC report viewer for an instant readable view.
Step 2: read report_metadata and policy_published first
The report_metadata block is the first main block in the DMARC aggregate report structure, after an optional version element, and names who sent the report and when.
report_metadata, names the reporting organisation (org_name), areport_id, and thedate_rangebegin and end as Unix epoch seconds, which you should convert to real dates first (RFC 9990).policy_published, shows the domain and thepvalue the receiver evaluated against; it can still show your previous policy for a day or two after a DNS change (RFC 9990).- No
pctfield in an RFC 9990 report, RFC 9990 dropped it, and you may seenp,discovery_method(pslortreewalk) ortestinginstead;testingmirrors the record's ownt=tag, and witht=yfailing mail should be treated one enforcement level below your published policy (RFC 9990;t=yis in RFC 9989). Reports still built on the older RFC 7489 format normally carry apctfield, and that is not an error either. date_rangewill not always read as 24 hours, the period is typically one UTC day starting at 00:00 UTC, but receivers can report more often than daily (RFC 9990).
Pro Tip: Convert every date_range begin and end value from Unix epoch seconds into your own timezone before comparing a report against your mail logs, since a reporting period typically runs from midnight UTC, not your local midnight.
Step 3: read each record row
Each record element covers a single source IP: read its row, identifiers and auth_results in turn.
row, holdssource_ip,countandpolicy_evaluated, which carries the disposition plus the DMARC-aligneddkimandspfresults (RFC 9990).disposition, reads none, pass, quarantine or reject: RFC 9990 added pass, for mail that already passed DMARC under an enforcing policy, not just the original three (RFC 9990).- The
dkimandspfvalues underpolicy_evaluated, are DMARC-aligned results, not the raw authentication outcome: the per-signature result, with its own domain, selector and result, sits inauth_results(RFC 9990). identifiers, gives theheader_fromdomain shown to the recipient, plus optionalenvelope_fromandenvelope_to; a blankenvelope_fromon a bounce message is normal, not a broken report (RFC 9990).source_ip, is the last server that handed off the mail, not necessarily where it originated, so an unfamiliar IP can be a forwarder rather than spoofing.
Step 4: decide what each row means for your domain
What a row means for your domain depends on whether you recognise the source, and whether it aligned.
- A recognised source with an aligned pass, needs no action: that sender is authenticated correctly.
- A recognised source with an aligned fail, such as a CRM or marketing platform, needs DKIM signing set up with your own domain, or a bounce (MAIL FROM) domain under your domain whose own SPF record authorises the platform's servers, then checked again on the next report.
- A
reasoncode such aslocal_policy,mailing_list,other,policy_test_modeortrusted_forwarder, explains a disposition that differs from your published policy, and appears only then (RFC 9990). - An unaccounted-for IP, is worth investigating first: check it against every sending service you use before assuming abuse.
- One row in one report, is a lead, not a verdict: a pattern across several days and receivers is what confirms a genuine problem.
How to check you have read the report correctly
You have read the report correctly once you can restate its metadata and account for every source IP.
- State, in your own words, which organisation sent the report, the exact date range in real dates, and the policy it evaluated against; if you cannot, reopen
report_metadataandpolicy_published. - Sort every
source_ipinto a bucket: recognised and aligned, recognised and now fixed, or flagged to investigate, with none left unsorted. - Cross-check your reading by dropping the file into the free DMARC report viewer, which decodes
.xml,.gzor.zipfiles up to 10 MB entirely in the browser. - Confirm the live record itself, rather than a past report, with the SPF, DKIM and DMARC checker, which validates it against RFC 9989 directly.
For a wider check than DMARC alone, the free Security Grade tool grades the domain A+ to F from up to 16 outside-in checks.
Common mistakes when reading RUA reports
A common cause of misreading a RUA report is carrying habits from RFC 7489 (the earlier, informational DMARC specification that RFC 9989 replaced) into an RFC 9990 aggregate report.
- A report with no record rows is not normal, RFC 9990 requires at least one; a file with none is a receiver error, or only the metadata was opened (RFC 9990).
- Disposition is not only none, quarantine or reject, RFC 9990 added pass, which older guides written against RFC 7489 do not mention (RFC 9990).
- An RFC 9990
policy_publishedhas nopctfield, so expectnp,testingordiscovery_methodinstead, though a report still on the older RFC 7489 format normally shows one; either way, do not addpctback into your DNS record, since RFC 9989 removed it entirely (RFC 9989). dkimandspfinsidepolicy_evaluatedare not raw results, they are DMARC-aligned outcomes; the real per-signature result sits inauth_results(RFC 9990).- A gap in one receiver's reports proves nothing, reports arrive only from receivers that choose to send them, on the days they see your mail.
Keeping RUA reporting healthy over time
RUA reporting stays useful only if you keep reopening it as your sending sources change, such as after adding or dropping a marketing platform or helpdesk; a newly aligned or newly failing source_ip is usually the first sign. Review reports weekly while you are still finding senders, then monthly once every source is accounted for.
On Professional and MSP, a DMARC health monitor re-reads your published DMARC record every 5 minutes and emails you only when the effective p= policy changes; it watches your own record, not the reports. Once every source has stayed aligned long enough for your comfort, use that evidence to move from p=none (monitoring only) to p=quarantine (send fakes to spam) and then p=reject (the strictest policy, though receivers may still send fakes to spam rather than refuse them); the none to reject guide covers that move in full.
Start reading your own reports today
You can start today, with a report file in hand or before your first one arrives.
If you already have a report file, drop it into the free DMARC report viewer, which parses it in your browser without installing anything. If you have not published a rua= tag yet, generate a valid record with the free DMARC generator instead.
ShieldMarc's own dashboard ingests DMARC reports automatically: the free Starter plan covers aggregate reports for a single domain, and a 30-day trial with no card covers up to 10 domains and every module except Dynamic SPF. The free viewer decodes aggregate XML only; forensic (RUF) reports need the RUA and RUF guide instead.
My view: ShieldMarc's report ingest exists because reading a fresh XML file by hand stops scaling past one domain. If you watch a single domain, the free viewer is enough; the dashboard earns its place once you would rather be told your policy changed than go looking for it.
Frequently asked questions
Why does my DMARC report show zero records?
Because RFC 9990 requires at least one record in every valid aggregate report, zero records usually means you have only opened the metadata, or the receiver sent a malformed file (RFC 9990). A receiver who saw no mail from your domain typically sends no report at all that day, rather than an empty one.
What is the difference between the dkim and spf results in policy_evaluated and in auth_results?
policy_evaluated holds the DMARC-aligned result that decided the disposition, while auth_results holds the raw per-check result, including the DKIM domain and selector or the SPF domain, before alignment is applied (RFC 9990). A raw pass in auth_results can still show as an aligned fail in policy_evaluated when the signing or sending domain does not match the visible From address.
Do all mailbox providers send DMARC aggregate reports?
No. Reports come only from receivers that choose to send them, on days they saw mail from your domain. A gap in reports from one provider does not prove that no mail was sent, or that nothing failed. Judge a sender across several receivers and several days, not from one provider's silence.
Can I read a DMARC XML report without installing anything?
Yes. Once decompressed, the file is plain XML, readable in any text editor or browser. Nested tags become hard to scan by eye once a report has more than a few records, so the free DMARC report viewer parses the same file in your browser, with nothing to install.
