9 June 2026 · The DMARC Engine Team · 12 min read
The XML that nobody asked for
You publish a DMARC record, you wait a day, and then your inbox fills up with attachments named things like google.com!yourdomain.com!1718841600!1718928000.xml.gz. Inside each one is a wall of XML that looks like it was designed to be ignored. Most people open one, squint at it, and quietly create a mail filter to make them disappear.
That is a mistake, because those files are the whole point of DMARC. The DMARC record you published does almost nothing useful on its own at p=none. What it does is ask every receiver in the world to send you a daily summary of who is using your domain in the From address, whether those messages passed authentication, and what the receiver did with them. That summary is the aggregate report, also called the RUA report after the rua= tag that requests it. Learn to read one and you can see your entire email estate, including the parts you forgot existed and the parts that are not yours at all.
This article walks through a real aggregate report field by field. By the end you will be able to open the raw XML, find the rows that matter, and tell the difference between a legitimate service you misconfigured and an actual spoofer trying to impersonate you. We will also cover why a tool that turns the XML into plain English saves you from doing this by hand every morning.
What an aggregate report is and is not
First, set expectations, because the most common disappointment with DMARC reports is expecting the wrong thing.
An aggregate report is a statistical roll-up, not a log of individual emails. A receiver like Google or Microsoft takes every message it saw from your domain over a 24 hour window, groups them by a handful of properties (source IP, authentication results, the policy it applied), counts how many fell into each group, and sends you one compact row per group. You will never see subject lines, recipient addresses, or message bodies in an aggregate report. That detail lives in forensic or failure reports (the ruf= tag), which most large providers no longer send for privacy reasons.
So an aggregate report answers questions like "how many messages claiming to be from your domain came from this IP, and did they authenticate" and not "what did the spoofed email actually say". That is fine. For getting to a safe enforcement policy, the aggregate data is exactly what you need, and it is what tells you when it is safe to move from p=none to p=reject without breaking email.
If your reports are arriving empty or you are not getting any at all, that is a separate problem with the record itself, and we cover it in why your DMARC reports are empty. For the rest of this piece we assume the reports are flowing.
The shape of the file
Every aggregate report follows the same XML schema. There are three logical parts: the report metadata, the published policy, and one or more record rows. Here is a trimmed but realistic example. Read it once, then we will take it apart.
<feedback>
<report_metadata>
<org_name>google.com</org_name>
<email>noreply-dmarc-support@google.com</email>
<report_id>14820937502342342342</report_id>
<date_range>
<begin>1718841600</begin>
<end>1718928000</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>1842</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>
</feedback>
That is one row. A busy day might contain a dozen rows in one file and you might receive twenty files from twenty receivers. The structure never changes, so once you can read this you can read all of them.
The report metadata: who sent it and for when
The report_metadata block tells you the provenance of the report.
org_nameis the receiving organisation that generated it.google.comcovers Gmail and Google Workspace,Yahoocovers Yahoo and AOL,Enterprise OutlookandMicrosoftcover Outlook.com and Microsoft 365. You will quickly recognise the regulars.emailis the address the report came from, useful only if you want to reply or whitelist it.report_idis a unique identifier for that specific report. It matters when you are deduplicating or referencing a report in a support ticket.date_rangeis the window the report covers, expressed as Unix timestamps.1718841600is2024-06-20 00:00:00 UTCand1718928000is2024-06-21 00:00:00 UTC, so this report covers all of 20 June. Almost every provider uses a 24 hour window aligned to midnight UTC.
The single most important habit here is to mind the date range. Reports arrive a day or two late, and providers batch them differently. If you make a DNS change on Tuesday and panic at a failing row on Wednesday, check the timestamps: that row may describe Monday's traffic, before your change existed.
The published policy: a mirror of your own record
The policy_published block is the receiver telling you what DMARC record it found for your domain when it processed this traffic. This is genuinely useful as a sanity check, because it shows you what the world actually sees, not what you think you published.
domainis the domain the policy applies to.pis your main policy:none,quarantine, orreject.spis your subdomain policy if you set one. If you run mail from subdomains, this is worth watching, and there is more detail in our piece on the subdomain DMARC record.pctis the percentage of messages your policy asks to be subject to enforcement. Atpct=100it applies to everything.adkimandaspfare the alignment modes,rfor relaxed andsfor strict. These two letters decide whether a passing SPF or DKIM check actually counts for DMARC, and they trip up more people than anything else in the report. We come back to alignment in its own section because it is the concept that makes everything else make sense.
If the published policy in the report does not match the record you think you have, you have a DNS problem: a typo, a record that did not propagate, or a second conflicting record. That alone is worth opening a report for. For the meaning of each tag in your own record, see understanding your DMARC record.
The record row: where the truth lives
Now the part that matters. Each record describes one group of messages that shared the same characteristics. It has three sub-parts.
The row block: source, count, and disposition
source_ipis the IP address that actually connected and sent the mail. This is your starting point for identifying a source. A reverse DNS lookup or a quick WHOIS on the IP usually reveals the sender.209.85.220.41belongs to Google.198.2.x.xranges often belong to Mailchimp or Mandrill. An IP in a residential or unfamiliar foreign block, sending mail you cannot account for, is your first hint of a spoofer.countis how many messages fell into this group during the window. A count of1842from your own outbound server is normal. A count of3from a random IP in another country is a probe. A count of40000from an IP you have never heard of is either a forwarder or an attack, and the rest of the row tells you which.policy_evaluatedis what the receiver actually did and why. Thedispositionis the action taken:none(delivered normally),quarantine(sent to spam), orreject(blocked). Atp=nonethe disposition will always benoneregardless of whether authentication passed, which is exactly whyp=noneprovides a false sense of security: nothing is being stopped yet, you are only watching.
Inside policy_evaluated there are also dkim and spf results, and here is the trap that catches everyone: these are the DMARC-aligned results, not the raw authentication results. A pass here means the check passed and it was aligned to your domain. These are the numbers that decide your DMARC outcome.
The auth_results block: the raw verdicts
The auth_results block shows the underlying SPF and DKIM checks before alignment is considered.
- The
dkimresult lists the signingdomain, theselector, and theresult. The selector is helpful for identifying which service signed the message:selectorvalues likegoogle,s1,k1,mailchimp,selector1are signatures of specific platforms. - The
spfresult lists the domain that was checked (this is the envelopeMAIL FROMdomain, also called the return-path) and the result.
The difference between auth_results and policy_evaluated is the whole game, so let us make it concrete.
Alignment: the concept that explains everything
DMARC does not care that SPF or DKIM passed. It cares that they passed for a domain that matches the one in the visible From address. That matching is called alignment, and it is the single idea that separates "this email is genuinely from your domain" from "this email passed some checks but is wearing your name as a costume".
Consider a marketing email sent through a third-party platform. The recipient sees From: news@yourdomain.com. Under the bonnet:
- SPF is checked against the envelope return-path, which the platform sets to something like
bounces@mail.theplatform.com. SPF passes, because the platform's record authorises its own servers. But the SPF domain istheplatform.com, notyourdomain.com. SPF passes but is not aligned. In the report,auth_results/spf/resultispasswhilepolicy_evaluated/spfisfail. - DKIM is checked against the signature. If the platform signs with its own domain,
d=theplatform.com, DKIM passes but again is not aligned. If instead you set up DKIM properly so the platform signs asd=yourdomain.comwith a delegated selector, then DKIM passes and aligns, and DMARC is satisfied.
This is why a perfectly legitimate service can show up failing DMARC in your reports. The mail is real, the checks pass, but nothing is anchored to your domain, so DMARC correctly says "I cannot confirm this is you". The fix is configuration, not panic: get the service to sign DKIM as your domain, or set a custom return-path so SPF aligns.
Alignment mode controls how strict the match must be. With relaxed alignment (adkim=r, aspf=r, the default), the organisational domain must match, so mail.yourdomain.com aligns with yourdomain.com. With strict alignment (s), the domains must be identical. Relaxed is right for almost everyone. Strict alignment quietly breaks subdomains and is a frequent cause of self-inflicted failures.
DMARC passes if either SPF is aligned-and-passing or DKIM is aligned-and-passing. You do not need both. In practice DKIM alignment is the one to rely on, because DKIM survives forwarding while SPF does not.
Telling a misconfigured friend from a real foe
Now we can do the thing the whole report exists for: classify each row. Walk through these questions for every failing row.
1. Do you recognise the source IP? Look it up. If it resolves to a provider you use (your mail server, your CRM, your newsletter tool, your helpdesk, your invoicing system), it is almost certainly a legitimate source you have not finished authenticating. If it resolves to something you have no relationship with, raise an eyebrow.
2. Does anything pass, even unaligned? A legitimate-but-misconfigured source usually shows auth_results/spf or auth_results/dkim as pass, just not aligned. That tells you the sender controls real infrastructure with valid SPF or DKIM, they are simply not signing as you. A pure spoofer typically has both raw results failing: no DKIM signature at all, and SPF either failing or referencing a return-path that has nothing to do with you.
3. What is the volume and shape? Legitimate sources are steady and predictable: your newsletter tool sends thousands on the days you send a newsletter and little otherwise. Spoofing runs often appear as bursts of low-count rows from many scattered IPs, or one large spike from a single hostile IP, with a header_from of your domain and no aligned authentication anywhere.
4. Is it forwarding? A special, common case. Mail you legitimately sent gets forwarded by a recipient (a .forward rule, a mailing list, a university.edu alias). Forwarding breaks SPF because the forwarding server is not in your SPF record, but DKIM usually survives. So forwarded mail tends to show spf: fail (unaligned, different IP) but dkim: pass and aligned. Because DMARC passes on DKIM alone, these rows pass DMARC overall and are nothing to worry about. If you see spf failing but dkim passing and aligned, that is the fingerprint of forwarding, not fraud.
Put together, a legitimate misconfigured source looks like: a recognisable IP, at least one raw check passing, sometimes alignment on one mechanism, consistent volume. A spoofer looks like: an unknown IP, both raw checks failing or absent, a From address of your exact domain, and erratic or hostile volume. The report gives you every one of those signals in the row.
A worked example. Suppose you see three failing rows:
203.0.113.10, count 5,200,dkim: passaligned,spf: fail. That is your newsletter platform after you set up DKIM delegation, with SPF unaligned because the return-path is theirs. DMARC passes on DKIM. Leave it alone, or align SPF too for belt and braces.198.51.100.7, count 60,dkim: none,spf: passbut for domaincrm-sender.com. That is a CRM you forgot to authenticate. The mail is real but anchored to the vendor, not you. Fix: enable DKIM signing as your domain in the CRM, following the steps for a proper DKIM setup.45.83.x.x, count 900,dkim: none,spf: fail,header_from: yourdomain.com, IP in a hosting range you have never used. Both raw checks fail and it is claiming to be you. That is a spoofer. This is exactly the traffic enforcement will block, and the reason to move to enforcement once your real sources are clean.
From raw XML to plain English
You can do all of the above by hand. For a week or two you should, because nothing teaches you the structure faster than reading the raw file. But the format is hostile to humans at scale. Reports arrive gzipped or zipped, sometimes a dozen a day, each with multiple rows, each row needing an IP lookup and an alignment judgement. Doing this manually every morning is how DMARC projects stall: people stop reading the reports, the data piles up, and the domain sits at p=none for years, monitored by nobody.
This is what a report analyser is for. Paste or upload the XML and it does the mechanical work: decompresses the file, parses every row, resolves each source IP to a recognisable sender name, separates aligned passes from unaligned passes, groups your traffic by source, and flags the rows where authentication genuinely failed. Instead of 209.85.220.41 / count 1842 / dkim pass / spf pass you get "Google Workspace, 1,842 messages, fully authenticated and aligned, passing DMARC". Instead of a scattered set of failing rows you get "one unrecognised source sending 900 unauthenticated messages as your domain, likely spoofing". You can try this on a real report with the free DMARC report analyser, and check the policy side of things with the DMARC checker.
What a one-off analyser cannot do is watch the trend over time, and the trend is where the decision lives. Knowing it is safe to enforce means watching the same sources report clean for several consecutive days, and being alerted the moment a new source appears or a known one starts failing. That continuous view, with change alerts by email, is the job of ongoing monitoring, and it is what turns a folder full of XML into a clear yes or no on enforcement.
Common patterns you will see in week one
A few recurring shapes, so you recognise them on sight:
- Your own server, fully passing and aligned. The baseline. Make sure the count roughly matches your real outbound volume.
- A SaaS sender passing DKIM aligned but failing SPF. Normal and fine. DMARC passes on DKIM. Align SPF only if you want tidiness, and mind your SPF lookup limit while you do.
- A SaaS sender passing SPF for the vendor's domain, no DKIM. The classic misconfiguration. Real mail, not anchored to you. Enable DKIM signing as your domain.
- Forwarded mail: SPF fail, DKIM pass aligned. Harmless. DKIM survives forwarding; SPF does not.
- Low-count bursts from scattered unknown IPs, both checks failing, From of your domain. Spoofing or probing. Harmless while you read it at
p=none, blocked once you enforce.
The practical takeaway
A DMARC aggregate report is a daily census of everyone sending mail as your domain. The metadata tells you who reported and for when. The published policy confirms what the world sees. Each record row gives you a source IP, a message count, the action taken, and two sets of authentication results: the raw auth_results and the DMARC-aligned policy_evaluated. The gap between those two is alignment, and alignment is what separates "genuinely from your domain" from "passed some checks while wearing your name".
Read a few reports by hand to learn the structure, then let a tool carry the load. Drop a real report into the DMARC report analyser to see the XML turned into a clear list of who is sending as you and which sources need fixing. When you are ready to keep an eye on it continuously and be told the moment something changes, that is what the hosted DMARC service and emailed monitoring are built to do, so you reach p=reject without breaking email and stay there.