Guides
How to read a DMARC aggregate XML report
18 October 2026
An aggregate DMARC report is the XML file mailbox providers send to the address in your rua tag. It counts messages that used your From domain, with the authentication result and the policy that applied. It is not a copy of each message, and it is not an inbox-placement test.
What arrives in the mailbox
RFC 7489 section 7.2 describes the report. If rua is missing, receivers must not generate aggregate reports. When it is present, the usual transport is email. The XML should be gzip-compressed, with a filename shaped like receiver!example.com!begin!end.xml.gz. The timestamps are seconds since 1970-01-01 UTC. The subject line should look like Report Domain: example.com Submitter: mail.receiver.example Report-ID: ….
Receivers must be able to send daily reports. The default interval (ri) is 86400 seconds. Anything more frequent is best-effort. Reports are easier to line up when each one starts at 00:00 UTC, which the RFC asks generators to do for a 24-hour period.
The message carrying the report is itself supposed to pass DMARC with an aligned pass. That is there so you are less likely to process a report someone forged. It is a check on the report mail, not a promise about your own campaigns.
The three parts of the XML
The schema in Appendix C puts the document under a feedback element. Three children matter for a first reading.
report_metadata
Who produced the file, and which hours it covers.
org_nameandemailidentify the receiver that generated it.extra_contact_infois optional.report_idis that receiver’s id for this file. The same id in the subject helps you drop duplicates.date_rangehasbeginandend, both Unix seconds in UTC. Convert them before you compare two files. They are not calendar dates as text.erroris optional and can repeat when the generator wants to report a problem of its own.
policy_published
The DMARC policy the receiver found for the domain, not a guess about what you wish you had published.
domainis where the record was found.adkimandaspfarer(relaxed) ors(strict).pisnone,quarantine, orreject.spis the subdomain policy, when one was published.pctis the percentage of messages the policy applies to. The default in the record is 100.fois the failure-reporting option tied to forensic reports, below.
If you changed the TXT record during the reporting window, a single file can still contain one policy_published element while the rows reflect a mix of the old and new policy. The RFC tells consumers to expect that. Sort by date range before you conclude the receiver ignored an edit.
record, and the row inside it
Each record is a group of messages that shared a result. It has three children.
row holds:
source_ip, the connecting IP.count, how many messages fell into this group.policy_evaluated, withdisposition(none,quarantine, orreject), plusdkimandspf. Those two are the DMARC results:passorfail, after alignment. They are not the raw SPF or DKIM results.- An optional
reason. The type can beforwarded,sampled_out,trusted_forwarder,mailing_list,local_policy, orother. This is how a receiver explains a disposition that does not match thepyou published.
identifiers holds header_from (the domain in the From header) and envelope_from (the MAIL FROM domain). envelope_to is optional.
auth_results holds the raw checks:
- Zero or more
dkimentries. Each hasdomain(thed=domain), an optionalselector(thes=value), andresult. DKIM results includenone,pass,fail,policy,neutral,temperror, andpermerror. - At least one
spfentry, withdomain,scope(heloormfrom), andresult. SPF results includenone,neutral,pass,fail,softfail,temperror, andpermerror.
Read auth_results and policy_evaluated as a pair. A raw SPF pass on a domain that does not align shows up as auth_results/spf/result of pass and policy_evaluated/spf of fail. That combination is the whole alignment story in one row.
Unauthorized source, or a legitimate sender set up wrong
Use source_ip, the raw results, and whether you recognize the sender. The count tells you whether it is a trickle or your actual mail.
Unauthorized
The IP is not one you or your providers operate. header_from is your domain. Raw SPF is fail, softfail, or none, and DKIM is none or fail. Both policy_evaluated results are fail. Nobody authorized that host, and nobody signed as you.
With p=none, disposition can still be none. You asked for no specific action, so a failure row with disposition none is expected. It means the receiver recorded the failure. It does not mean the message was legitimate. That is the reason to stay on p=none until these rows are ones you understand, and then move toward quarantine or reject.
A legitimate sender, misconfigured
You know the IP, or the dkim/domain and selector match a provider you pay. The raw check may even pass, on the wrong name.
- SPF
passwithdomainset to a bounce host the provider owns,header_fromset to your brand, andpolicy_evaluated/spfoffail. The host was authorized for the provider’s domain. It was not aligned with yours. If that provider sends with your domain in MAIL FROM, their include belongs in your single SPF record. If they send with their own bounce domain, you need a passing aligned DKIM signature instead, because SPF will not align. - DKIM
passwithdomainset to the provider, whileheader_fromis your domain, andpolicy_evaluated/dkimoffail. The signature is valid and unaligned. Publish the selector they document, on your domain, and send again so a new row showsd=as you. - DKIM
permerrororfailon your own selector, at highcount. That is your mail, broken: a missing key, a rotated selector you have not published, or a body the sender rewrote after signing. Fix the DNS the provider is checking. Do not treat the IP as an attacker because the result string says fail.
A reason of forwarded or mailing_list means the receiver overrode the published policy for a message that was altered in transit. Read it before you decide the sender is hostile. Forwarding and lists break DKIM and SPF in ways your DNS cannot prevent.
Forensic reports (ruf), briefly
Section 7.3 describes failure reports, requested with the ruf tag. They are meant to go out soon after a DMARC failure, in the authentication-failure format the rf tag names (AFRF by default). They can include headers and URIs from the failed message, which is why they are more sensitive than a daily count. The fo tag asks when to send one. The default, 0, is when every underlying mechanism failed to produce an aligned pass. fo is ignored if ruf is absent.
If you do not publish ruf, receivers must not send failure reports. If you do, a receiver sends them only when it is willing. The same section notes that an attacker can forge your domain, fail authentication on purpose, and try to flood the ruf mailbox. Aggregate rua reports cover mail that passed and mail that failed, without a copy of each message. Read those first.
Sources
- RFC 7489: DMARC — section 7.2 (aggregate reports), section 7.3 (failure reports), Appendix C (XML schema), section 6.3 (rua, ruf, fo, p)
- RFC 7208: SPF
- RFC 6376: DKIM
- Google: Email sender guidelines — DMARC reports are recommended
- Yahoo Sender Hub: Sender Best Practices — rua is strongly recommended for bulk senders during setup