21 June 2026 · 14 min read
A DMARC aggregate report is a small XML file that tells you, source by source, exactly how mail claiming to come from your domain authenticated over a single day. It is the only honest, comprehensive view you will ever get of who sends email as you: your own mail servers, your marketing platform, your helpdesk, your payroll provider, and anyone forging your domain. The catch is that nobody designed these reports to be read by a human. They arrive gzip-compressed, full of nested tags and raw IP addresses, and the one fact you actually want, "is my real mail passing and is anyone spoofing me", is buried under a lot of mechanical detail. This article walks through a real aggregate report from top to bottom: the metadata header, the published policy, each record, the source IP, the SPF and DKIM results, alignment, and the disposition the receiver applied. By the end you will be able to open any aggregate report and know precisely what it is telling you, and what to do next.
If you only ever take one thing from a report, take this: an aggregate report is not a copy of your email. There are no subject lines, no message bodies and no recipient addresses inside it. It is a statistical summary of authentication outcomes, grouped by sending source over a reporting window. That distinction matters because people sometimes refuse to publish a rua= address fearing it leaks content. It does not. It leaks counts.
How a report arrives and what it is wrapped in
You receive aggregate reports because your DMARC record contains a rua= tag, for example rua=mailto:reports@yourdomain.com. Every mailbox provider that receives mail claiming to be from your domain, and that honours DMARC reporting, will send you one file per day summarising what it saw. Google, Microsoft, Yahoo, Comcast, Mail.ru, La Poste and a long tail of regional ISPs all do this on their own schedule.
The file almost always arrives compressed. The most common form is gzip, producing a .xml.gz attachment; some providers send a .zip. Decompress it and you have a single XML document. The filename itself follows a loose convention, usually receiverdomain!yourdomain!starttimestamp!endtimestamp.xml.gz, so you can often read the reporting provider, your domain and the time window straight from the name before you open anything.
If you want to see this decoded without building anything, paste a single file into the DMARC report analyzer; it decompresses the XML and renders it as readable rows. To confirm your own record even has a working reporting address in the first place, run the DMARC checker against your domain and look for a valid rua= tag. A record with no rua= produces no reports at all, which is the single most common reason a domain that "has DMARC" still cannot see anything.
The shape of the XML
Every aggregate report follows the same structure defined by the DMARC specification. There are three things to find at the top, then a repeating body. Here is a trimmed but representative example so you have something concrete to refer to as we go:
<feedback>
<report_metadata>
<org_name>google.com</org_name>
<email>noreply-dmarc-support@google.com</email>
<report_id>14955129635591931999</report_id>
<date_range>
<begin>1718668800</begin>
<end>1718755200</end>
</date_range>
</report_metadata>
<policy_published>
<domain>yourdomain.com</domain>
<adkim>r</adkim>
<aspf>r</aspf>
<p>none</p>
<sp>none</sp>
<pct>100</pct>
</policy_published>
<record>
<row>
<source_ip>209.85.220.41</source_ip>
<count>312</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>
<dkim>
<domain>yourdomain.com</domain>
<selector>google</selector>
<result>pass</result>
</dkim>
<spf>
<domain>yourdomain.com</domain>
<result>pass</result>
</spf>
</auth_results>
</record>
<!-- more <record> blocks follow -->
</feedback>
Three sections matter: report_metadata tells you who sent the report and when, policy_published tells you what policy that receiver saw on your domain, and one or more record blocks describe the actual mail. We will take them in turn.
Reading the metadata header
The <report_metadata> block answers "where did this come from and what period does it cover".
org_nameis the reporting organisation. In the example it isgoogle.com, so this report is Google's view of mail you sent that Google received. You will accumulate one of these per provider per day, and the same sending source will appear in several of them, each counting only the mail that particular provider saw.emailis the address the provider sends from. You do not reply to it; it is informational.report_idis a unique identifier for this specific report. It matters when you are de-duplicating, because providers occasionally send the same report twice and you do not want to count the mail twice. Two files with the samereport_idfrom the sameorg_nameare the same report.date_rangegivesbeginandendas Unix epoch timestamps in UTC. The example covers1718668800to1718755200, which is a 24-hour window on 18 June 2024. Almost every provider uses a 24-hour window, but they do not align it to the same hour, so do not assume "midnight to midnight" in your timezone. Convert the epoch values before you reason about timing.
The header on its own tells you nothing about your mail's health. Its job is to let you attribute and reconcile: which provider, which day. Hold on to it.
Reading the published policy
The <policy_published> block is quietly one of the most useful parts of the whole report, and most people skip it. It records the DMARC policy that this receiver actually observed on your domain at the time it processed the mail. That is not necessarily the policy you think you published; it is the policy DNS served to that receiver.
domainis the domain the policy applies to, your organisational domain.adkimandaspfare the alignment modes for DKIM and SPF respectively.rmeans relaxed,smeans strict. In relaxed mode, a subdomain match is enough (mail fromnews.yourdomain.comaligns withyourdomain.com). In strict mode, the domains must match exactly. Most domains run relaxed, and you can read the full definitions of strict and relaxed alignment in the glossary.pis your domain policy:none,quarantineorreject. In the example it isnone, which means the receiver was instructed to take no action on failures, only report them. This is monitoring mode.spis the subdomain policy. If absent, subdomains inheritp. If present, as here withnone, it governs mail from subdomains explicitly.pctis the percentage of failing mail the policy asked the receiver to act on.100is the safe default. A lower value during rollout means only a sample of failing mail is quarantined or rejected.
Why does this matter so much? Because it is your early-warning system for record drift. If you believe you published p=reject but a fresh report shows p=none in policy_published, then either a DNS change has not propagated, or someone overwrote your record, or a registrar flattened it. The report is telling you what the world actually sees, which is the only version that protects you. If the published policy looks wrong, re-check the live record with the DMARC checker immediately.
Reading a record: the heart of the report
Each <record> block describes one distinct combination of sending source and authentication result, with a count attached. A busy domain produces many records per report, because every different combination of IP and outcome gets its own block. Inside a record there are three parts: row, identifiers and auth_results.
The row: source, count and disposition
<row>
<source_ip>209.85.220.41</source_ip>
<count>312</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
source_ipis the server that actually sent the mail. This is the single most important field for tracing a source.209.85.220.41is a Google sending IP, so this is mail sent through Google's infrastructure on your behalf. A raw IP means little at a glance, which is exactly why turning IPs into named services is the bulk of the work later.countis how many messages this exact source-and-result combination accounted for in the window. Here, 312 messages. The count is everything: a source failing authentication on two messages is noise, the same source failing on twelve thousand messages is a problem or an attack.policy_evaluatedis what the receiver decided to do, and the DMARC-level results it derived.dispositionis the action actually taken:none,quarantineorreject. Thedkimandspfvalues here are the DMARC-aligned results, not the raw authentication results. This is a subtle and frequently misread point, which we will pull apart below.
The identifiers: what the message claimed to be
<identifiers>
<header_from>yourdomain.com</header_from>
</identifiers>
The header_from is the domain in the visible From address, the one a human reads in their mail client. DMARC is fundamentally about protecting this domain. Everything else, SPF and DKIM, exists to prove that the visible From domain is legitimate. Some reports also include envelope_from and envelope_to, but header_from is the identifier DMARC aligns against.
The auth_results: the raw authentication outcomes
<auth_results>
<dkim>
<domain>yourdomain.com</domain>
<selector>google</selector>
<result>pass</result>
</dkim>
<spf>
<domain>yourdomain.com</domain>
<result>pass</result>
</spf>
</auth_results>
This is the underlying detail that the policy_evaluated results were derived from.
- DKIM reports the
domainthat signed (thed=value in the signature), theselectorthat points to the public key, and theresult:passorfail. The signing domain here isyourdomain.comwith selectorgoogle, so your Google-sent mail is DKIM-signed under your own domain. You can look up any selector's published key with the DKIM checker. - SPF reports the
domainthat SPF checked and theresult:pass,fail,softfail,neutral,none,temperrororpermerror. Note that the domain SPF checks is the envelope (Return-Path / Mail From) domain, which is not always the same as the visible From domain. That gap is where alignment comes in. You can inspect a domain's SPF record and its lookup count with the SPF checker.
SPF pass, DKIM pass, and the alignment that decides everything
Here is the trap that catches almost everyone reading their first reports. A message can show SPF: pass in auth_results and still fail DMARC. This is not a bug. It is the whole reason DMARC exists.
SPF authenticates the envelope domain, the address in the SMTP MAIL FROM. That domain is invisible to the recipient. The recipient only ever sees the header_from. A spammer can send a message where the envelope domain is bounces.spammer.example (which has its own valid SPF record, so SPF passes) while the visible From reads ceo@yourdomain.com. SPF passed, but it passed for the wrong domain.
DMARC closes that hole with alignment. For a message to pass DMARC it needs at least one of:
- SPF alignment: SPF passed and the envelope domain matches the
header_fromdomain (exactly, in strict mode; allowing a subdomain, in relaxed mode). - DKIM alignment: DKIM passed and the signing
d=domain matches theheader_fromdomain under the same matching rules.
The dkim and spf values inside policy_evaluated are the aligned results. So when you read a record, compare the two layers:
- If
auth_resultsshowsspf: passfor domainyourdomain.comandheader_fromisyourdomain.com, then SPF is both authenticated and aligned, andpolicy_evaluated/spfwill readpass. - If
auth_resultsshowsspf: passfor domainmailer.vendor.examplebutheader_fromisyourdomain.com, then SPF authenticated but did not align, andpolicy_evaluated/spfwill readfail, even though the raw SPF passed.
A message survives DMARC if either SPF or DKIM is aligned and passing. This is why DKIM is the more durable of the two: DKIM signatures survive forwarding, whereas SPF breaks the moment a message is relayed and the envelope domain changes. When you see legitimate mail failing in reports, forwarding is one of the usual culprits, and the fix is almost always to ensure DKIM is aligned so the message still passes even when SPF cannot.
For a deeper treatment of why a message can fail DMARC while SPF and DKIM individually pass, the alignment vocabulary is laid out term by term in the glossary, and the practical implications for forwarded mail are covered in the product detail for DMARC.
Reading disposition: what the receiver actually did
The disposition inside policy_evaluated is the outcome that affected real recipients.
nonemeans the message was delivered normally despite any DMARC failure. This is what you see underp=none. Failures are reported but not acted on.quarantinemeans the receiver was instructed to treat the message with suspicion, typically routing it to spam or junk.rejectmeans the receiver was told to refuse the message outright, so it never reached the recipient.
Read disposition together with the aligned results. A record showing disposition: none with policy_evaluated/dkim: fail and policy_evaluated/spf: fail, under a published policy of p=none, is a message that would have been blocked had you been at p=reject. That is the single most important pattern to spot during rollout. Every such record is a question: is this a legitimate source I have not yet authenticated, or is it spoofed mail I want to block? You cannot move to enforcement until you can answer that for every failing source.
Turning records into action
Reading one record is easy. The value is in what you do across all of them. Here is the practical workflow, in order.
1. Group every record by source IP and resolve it to a service
A bare IP is meaningless to most people, so the first real task is identification. For each source_ip, work out what it is: your own mail server, Google Workspace or Microsoft 365, a marketing platform like Mailchimp or SendGrid, your CRM, your helpdesk, your invoicing tool. Reverse DNS, the IP ranges published in vendors' SPF records, and known sending infrastructure all help. Anything you cannot map to a service you recognise gets flagged as unknown, and unknown is exactly where your attention belongs. It is either shadow IT you forgot about or someone forging your domain.
2. Separate legitimate sources from impersonators
Now split the unknowns. A source sending a steady, modest volume that resolves to a recognisable cloud service is almost certainly a legitimate sender you forgot to authenticate, for instance an old monitoring box emailing alerts from an unsigned server, or a department that signed up to a SaaS tool without telling anyone. A source sending from residential IP ranges, botnet-style spread across many countries, with a 100 percent fail rate and no DKIM signature at all, is spoofing you. The aggregate report is where both become visible for the first time.
3. Authenticate every legitimate source
For each legitimate source that is failing alignment, fix the authentication. That usually means one of:
- Adding the sender's include or IP to your SPF record, while watching the ten-lookup limit so you do not break SPF with a
permerror. - Enabling and aligning DKIM for that sender, so the signing
d=domain matches your From domain. This is the more robust fix because it survives forwarding. - Moving the sender to a subdomain you control, so its mail aligns cleanly under relaxed alignment.
After each change, watch the next few days of reports. The failing record for that source should turn into a passing one. That feedback loop, change then verify in the next report, is the entire enforcement journey.
4. Only then tighten the policy
When the reports show that every legitimate source is authenticated and aligned, and the only remaining failures are sources you have positively identified as spoofing, you can safely raise the policy. Move from p=none to p=quarantine, watch the reports for a fortnight to confirm nothing legitimate broke, then move to p=reject. Because you read the reports first, the only mail your p=reject policy blocks is the mail you wanted it to block. This staged approach, anchored in report evidence, is what takes a domain to enforcement without an email outage. The full requirements for getting there are summarised on the requirements page.
A worked reading of the example
Return to the example report and read it as a whole. The metadata says this is Google's report for a 24-hour window on 18 June 2024. The published policy says Google observed p=none with relaxed alignment on both SPF and DKIM at pct=100, so this domain is still in monitoring mode. The single record shows 312 messages from 209.85.220.41, a Google IP, with header_from of yourdomain.com. DKIM passed and signed under yourdomain.com with selector google; SPF passed for yourdomain.com. Both aligned. The disposition was none, which under p=none simply means delivered.
In one sentence: this is 312 legitimate, fully authenticated and aligned messages sent through Google Workspace, and there is nothing to fix here. Multiply that reading across every record in every provider's report for a few weeks, and you have a complete map of your sending estate, which is precisely the map you need before you enforce.
Why this is worth automating
Reading one report by hand, as we just did, is genuinely instructive, and you should do it at least once so the format stops being a mystery. Reading thirty a day, every day, reconciling overlapping counts across providers, converting IPs to services and tracking pass rates over time, is not something anyone sustains by hand. That is why most domains that publish DMARC never escape p=none: the record monitors, the reports pile up unread, and the policy never blocks a single spoofed message. The reports are the entire point of DMARC, and they are the part almost everyone skips.
DMARC Engine does this reading continuously. Point your rua= tag at a managed reporting address and every inbound report is decompressed, validated, parsed into rows, de-duplicated against the report_id, reconciled across providers and enriched so each source IP shows up as a named service rather than a number. What you see is the answer, not the XML: a list of every source sending as you, its volume, its pass rate, and whether it is aligned. Unknown and failing sources are surfaced for you to act on, and the platform guides the staged move from p=none to p=reject off that evidence.
If you want to start by understanding your own record and confirming reports will actually flow, run the DMARC checker for the rua= tag and policy, the SPF checker and DKIM checker to see what each source needs to pass, and, for forwarding-heavy mail, the MTA-STS checker and BIMI checker once you reach enforcement. Then paste one real report into the DMARC report analyzer and read it through, line by line, the way we just did. Once the format clicks, the reports become the most useful thing your domain produces, and the path to a spoof-proof domain is simply a matter of acting on what they show you.