How to read DMARC aggregate reports
A DMARC aggregate report is an XML file that a receiving mail system sends, usually once a day, to the address in the rua tag of your DMARC record. It lists every IP address that sent mail with your domain in the From: header during the period, how many messages each sent, and whether SPF and DKIM passed and aligned with your domain. It contains no message content. Read it to find the services you forgot to authenticate before you tighten your policy, and to spot forgeries of your domain.
Where the format is defined
Aggregate reports were first specified in RFC 7489, section 7.2, with the XML schema in Appendix C. In May 2026 the IETF replaced RFC 7489 with three documents: RFC 9989 for DMARC itself, RFC 9990 for aggregate reporting and RFC 9991 for failure reports. The structure is the same in both versions. Reports built to RFC 9990 use the XML namespace urn:ietf:params:xml:ns:dmarc-2.0 and can carry the new np, testing and discovery_method elements; reports built to RFC 7489's schema can carry pct. A parser should accept both.
Who sends them and how they arrive
A receiver that supports DMARC finds your reporting request when it looks up your DMARC record for incoming mail. Without a rua tag it must not send aggregate reports at all (RFC 9989, section 4.7). You can list several addresses, separated by commas, and a report should go to each. Yahoo's sender best practices strongly recommend a rua address for bulk senders; see Gmail and Yahoo sender requirements.
RFC 9990, section 3.5.2, fixes the email format:
- The XML is attached, normally gzip-compressed (
application/gzip, file ending.xml.gz), otherwise astext/xml. - The file name joins the receiver, your domain and the start and end of the period, in seconds since 1970 (UTC), with
!between them. - The subject reads
Report Domain: <your domain> Submitter: <receiver>, optionally with aReport-ID:. - The report message must itself pass DMARC with alignment, which helps you discard forged reports.
A report covers one DMARC policy domain. Subdomains without their own _dmarc record appear as separate record entries in the report for the parent domain; a subdomain with its own record gets its own report (RFC 9990, section 3.1.2).
Expect volume. Google's About DMARC reports says large organizations can receive hundreds or even thousands of reports a day, and recommends a dedicated group or mailbox for them. RFC 9989 recommends parsing reports by machine rather than reading them by hand.
The main elements
| Element | What it holds |
|---|---|
report_metadata | Who sent the report (org_name, email), a unique report_id, the date_range (begin, end) and any error found in your record |
policy_published | Your record as the receiver saw it: domain, p, sp, alignment modes adkim and aspf, and newer or older tags |
record | One per combination of sending IP, results and identifiers; a report has at least one |
row | source_ip, the message count, and policy_evaluated |
policy_evaluated | The disposition applied (none, quarantine, reject, or pass in RFC 9990) and the DMARC verdicts for dkim and spf, plus an optional reason |
identifiers | header_from (the From: domain), and optionally envelope_from and envelope_to |
auth_results | Raw results: each dkim signature with domain, selector and result, and the spf check with domain and result |
The date_range marks the reporting period, not the first and last message, and RFC 9990 says it typically covers one UTC day starting at 00:00.
An annotated example
A shortened report for example.com, with documentation IP addresses:
<feedback xmlns="urn:ietf:params:xml:ns:dmarc-2.0">
<report_metadata>
<org_name>receiver.example</org_name>
<email>dmarc-reports@receiver.example</email>
<report_id>2026-11-05_example.com@receiver.example</report_id>
<date_range><begin>1793836800</begin><end>1793923199</end></date_range>
</report_metadata>
<policy_published>
<domain>example.com</domain>
<p>none</p><sp>none</sp><adkim>r</adkim><aspf>r</aspf>
</policy_published>
<record> <!-- 1: your own mail, fully aligned -->
<row>
<source_ip>192.0.2.25</source_ip><count>1840</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><selector>s1</selector><result>pass</result></dkim>
<spf><domain>bounce.example.com</domain><result>pass</result></spf>
</auth_results>
</record>
<record> <!-- 2: a platform signing with its own domain -->
<row>
<source_ip>198.51.100.7</source_ip><count>312</count>
<policy_evaluated><disposition>none</disposition><dkim>fail</dkim><spf>fail</spf></policy_evaluated>
</row>
<identifiers><header_from>example.com</header_from></identifiers>
<auth_results>
<dkim><domain>platform.example</domain><selector>k1</selector><result>pass</result></dkim>
<spf><domain>mail.platform.example</domain><result>pass</result></spf>
</auth_results>
</record>
<record> <!-- 3: forwarded copies -->
<row>
<source_ip>203.0.113.40</source_ip><count>6</count>
<policy_evaluated><disposition>none</disposition><dkim>pass</dkim><spf>fail</spf></policy_evaluated>
</row>
<identifiers><header_from>example.com</header_from></identifiers>
<auth_results>
<dkim><domain>example.com</domain><selector>s1</selector><result>pass</result></dkim>
<spf><domain>example.com</domain><result>fail</result></spf>
</auth_results>
</record>
</feedback>The period is 5 November 2026, 00:00 to 23:59:59 UTC. The receiver saw p=none, so nothing was quarantined or rejected, whatever the results.
- Record 1 passes both ways. The SPF domain
bounce.example.comaligns withexample.comunder relaxed alignment (aspf=r), because both share the organizational domain. - Record 2 is the case to fix. Both raw checks pass in
auth_results, but for the platform's domains, so neither aligns, andpolicy_evaluatedshowsfailtwice. Atp=rejectthese 312 messages would be at risk. Set up DKIM signing with your own domain at the platform, or a custom bounce domain. - Record 3 fails SPF because the mail arrived from a server not in your SPF record, but your DKIM signature survived, so DMARC still passes. This is the typical pattern of forwarding.
How to read pass, fail and alignment
The key is the difference between the two blocks. auth_results holds the plain SPF and DKIM results, before DMARC looks at alignment. policy_evaluated holds the DMARC verdicts: pass only if the check passed for a domain that aligns with header_from (RFC 9990, section 3.1.1). DMARC passes if either verdict is pass.
auth_results | policy_evaluated | Likely meaning |
|---|---|---|
| DKIM and SPF pass for your domain | pass / pass | Correctly set up |
| Pass, but for another domain | fail / fail | A service you use without alignment |
| DKIM passes for you, SPF fails | pass / fail | Forwarding; DMARC still passes |
| DKIM fails, SPF fails or another domain | fail / fail | A mailing list that changed the message, or a broken signature |
| No DKIM, SPF fails, unknown IP | fail / fail | Possible forgery, or a sender nobody told you about |
To identify a source IP, look up its PTR name (see reverse DNS) and compare it with the services your company uses. The DKIM selector often points to the service too; see DKIM selectors and key rotation. Look at counts: a few messages from an unknown IP matter less than a steady stream.
Forwarding and mailing list noise
Forwarders send your mail on from their own IP addresses, so SPF fails for those copies, while DKIM usually survives. Mailing lists that add a subject tag or footer break DKIM as well (RFC 7960, sections 2.2 and 3.2.3). Such rows will appear in your reports with small counts from IP addresses you have never heard of. Do not add those IPs to your SPF record: they are not yours, and aligned DKIM is the fix that carries through forwarding.
When a receiver applies something other than your policy, it can say why in policy_evaluated/reason. RFC 9990, section 3.1.6, defines the types local_policy, mailing_list, trusted_forwarder, policy_test_mode (your record has t=y) and other; RFC 7489 also had forwarded and sampled_out (excluded by pct). Google's report page mentions accepting unauthenticated mail because it passed ARC as one such action.
Sending reports to another domain
If the rua address is in a different organizational domain from your record, for example a report-processing service, the receiver first checks that the destination agrees to take your reports (RFC 7489, section 7.1; RFC 9990, section 4). It looks for a TXT record named after your domain under _report._dmarc at the destination:
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc@reports.example.net"
example.com._report._dmarc.reports.example.net. TXT "v=DMARC1"The second record is published by the owner of reports.example.net. Without it, receivers ignore that address. A destination willing to accept reports for any domain can publish a wildcard, *._report._dmarc.reports.example.net. If you send reports to an address within your own organizational domain, no extra record is needed.
Failure reports (ruf)
The ruf tag requests failure reports: one report per failing message, or per group of similar ones, sent soon after the failure and carrying the message's header fields or even its full content (RFC 9991, sections 1 and 2). The fo tag chooses when they are sent.
Expect few of them. Google's Set up DMARC page says Gmail does not support ruf, and RFC 9991, section 7, notes that many large providers limit or disable failure reports because of privacy concerns. Aggregate reports are the dependable source.
Privacy
Aggregate reports contain domain-level data: sending IP addresses, counts and authentication results. RFC 9990, section 7.2, says they do not include individual email addresses or message content. Failure reports are different: they may contain personal data from the message, and RFC 9991 recommends limiting their use to targeted diagnosis and redacting content. If a third party processes your reports, it sees which services send your mail and how much; RFC 9990, section 7.1, asks both domain owners and receivers to check whether their own policies permit that.
Questions
How soon do reports arrive after I publish a rua address?
Reports usually cover a full UTC day and are sent after it ends, so the first ones arrive after the first full day with the record in place. Only receivers that support DMARC reporting and received your mail send one.
Why do the counts not match what I sent?
Each receiver reports only the mail it received, and only receivers that send reports appear. Forwarded copies add rows. Treat totals as a sample of your traffic, not an exact count.
Do I need a paid service to read them?
No. RFC 9989, section 5.1.5, mentions both third-party services and your own tools; the XML is documented, so a small domain can parse it with a script. Google suggests a service that specializes in DMARC, since it combines and analyzes reports from many receivers.
Try it
Check whether your record has a valid rua address with the DMARC checker, or write one with the DMARC generator.
Related
- Glossary: SPF, DKIM and DMARC.
- SPF checker and DKIM checker for fixing the sources a report reveals.
- Bounce code 5.7.32: the From: domain is not aligned.
Related guides
- Moving DMARC from p=none to p=reject
- Gmail and Yahoo sender requirements
- DKIM selectors and key rotation
- Reverse DNS (PTR records) for mail servers
- MTA-STS and TLS reporting explained
- All guides
Sources
- RFC 9990, DMARC Aggregate Reporting (May 2026): XML elements, email format, external destinations, override reasons, privacy.
- RFC 7489, DMARC (March 2015): section 7.1 (external destinations), section 7.2 and Appendix C (the original report format).
- RFC 9989, DMARC (May 2026): the
ruatag and report handling. - RFC 9991, DMARC Failure Reporting (May 2026): failure reports and their privacy risks.
- RFC 7960, Interoperability Issues between DMARC and Indirect Email Flows: why forwarding and lists break SPF and DKIM.
- Yahoo: Sender best practices: the
ruarecommendation. - Google Workspace: About DMARC reports: report volume, dedicated mailbox, ARC overrides.
- Google Workspace: Set up DMARC: Gmail does not support
ruf.
How we wrote this: drafted by the email-validation.org team with the help of AI writing tools, from our product documentation and the sources linked in the text.