DMARC aggregate reports are often the first evidence a domain owner sees of who is actually sending email under their name, and the XML they arrive in is not friendly to read. This guide covers how to read DMARC reports field by field, how to tell a misconfigured legitimate sender from a spoofer, and how to use aggregate feedback to move safely from p=none to enforcement. If you already publish a DMARC record and a mailbox is filling up with zipped XML files, this is for you.
What Aggregate Feedback Actually Contains
Aggregate reports (the rua tag in your DMARC record) are summaries, not message copies. Each receiving mail system, such as Google, Yahoo, Microsoft’s consumer Outlook.com service, Comcast or Mail.ru, groups the messages it saw claiming your domain over a reporting window. It then sends you one XML file, usually gzip- or zip-compressed.
The default window is 24 hours (ri=86400). Some receivers ignore shorter intervals. A mid-sized domain that sends newsletters can expect 20-60 reports a day. A domain that only sends from Google Workspace might get 3-5.
Reports carry no subject lines, recipients or message bodies. They show which IP addresses sent mail using your domain in the From header, how many messages each sent and how those messages fared against SPF, DKIM and alignment.
Reading DMARC Reports Field by Field
An experienced email administrator reads a report from top to bottom in four blocks.
report_metadata tells you who sent the report (org_name), a unique report_id and a date_range. The begin and end values are Unix epoch timestamps, so 1729641600 means midnight UTC on 23 October 2024. Convert them before you try to match anything against your sending logs.
policy_published echoes back the DMARC record the receiver found: the domain, adkim and aspf (alignment modes, r for relaxed or s for strict), p, sp for subdomains and pct. Check this block first. If it shows p=none while you believe you published quarantine, the receiver saw a cached or different record, and that matters more than anything else in the file.
record/row holds the source_ip, a message count and policy_evaluated. That last element has three children: disposition (none, quarantine or reject), plus dkim and spf results. These two values are DMARC-aligned results, not raw authentication results.
auth_results holds the raw checks. It lists the DKIM domain and selector that signed the message and whether the signature verified, and the SPF domain (the envelope sender) and its result.
The gap between these last two blocks is where most of the useful diagnosis happens.
The Myth That an SPF Pass Means DMARC Passes
This is the most common misreading. A row can show spf result=pass in auth_results and still fail DMARC. The reason is alignment.
Say your marketing team sends through SendGrid without custom domain authentication. The envelope sender is something like bounces.sendgrid.net. SPF passes for sendgrid.net, but that domain doesn’t match the From header domain, example.com. policy_evaluated shows spf=fail. If DKIM is also signed with d=sendgrid.net, DMARC fails outright.
Under p=none nothing visible happens. Under p=reject every one of those newsletters disappears. Reading auth_results alone would have told you everything was fine.
A Realistic Rollout Scenario
A typical situation: a company of about 200 people publishes v=DMARC1; p=none; rua=mailto:dmarc@example.com in March and plans to enforce by June. Two weeks of reports show about 41,000 messages from Google’s IP ranges, all aligned. They also show about 6,000 from an IP block belonging to a CRM vendor and 900 from a single IP at a hosting provider.
The CRM traffic passes raw SPF but fails alignment. That’s legitimate mail that needs DKIM signing with the company’s own domain. The 900 messages turn out to be a forgotten WordPress server sending password resets through PHP mail() with no authentication at all. Neither source is malicious. Both would have broken silently under enforcement.
The order of work that follows is fairly standard:
1. Group report rows by source IP and resolve each IP to its owner (reverse DNS, WHOIS or ASN lookup).
2. Label each source as known-legitimate, unknown or clearly abusive.
3. Fix alignment for legitimate sources by adding a DKIM key on your domain or a custom return-path.
4. Wait at least two full weeks of clean reports, then move to p=quarantine with pct=25.
5. Raise pct in steps and switch to p=reject once failures from legitimate senders are near zero.
The full policy progression is covered in the DMARC implementation guide. The reports are what tell you when each step is safe.
Common Mistakes When Interpreting Aggregate Data
Treating forwarding failures as attacks. When mail passes through a mailing list or a university forwarding address, SPF breaks because the forwarding server’s IP isn’t in your record. DKIM usually survives unless the list changes the subject or footer. Rows with low counts from Google Groups, Microsoft forwarding ranges or .edu servers are almost always forwarding. Some receivers use ARC (Authenticated Received Chain) to account for this, but don’t count on it.
Pointing rua at a third-party domain without authorization. If your record sends reports to dmarc@reports.vendor.com, the vendor’s domain must publish a TXT record at example.com._report._dmarc.vendor.com containing v=DMARC1. Without it, compliant receivers silently drop your reports. Teams then assume nothing is wrong because nothing arrives.
Fixing SPF by adding includes until it works. Each new sender’s include pushes the record closer to the ten-lookup SPF ceiling. Past ten lookups, SPF returns permerror for everything. Aligned DKIM is usually the better fix for third-party senders because it doesn’t use up lookups and it survives forwarding.
A smaller mistake is watching only failures. A sudden drop in volume from your main mail provider can mean a broken DKIM selector after a key change. That’s especially likely if your team runs scheduled DKIM key rotation and someone removes the old selector too early.
Tooling and When Manual Reading Still Makes Sense
Reading raw XML works for a domain with one or two senders and a handful of reports a week. Beyond that, you need a parser. parsedmarc is a widely used open-source option. It reads a reporting mailbox over IMAP and writes to Elasticsearch, OpenSearch or Splunk, and Grafana or Kibana dashboards sit on top. Hosted DMARC report processors do the same thing without the infrastructure.
There are exceptions. Organizations in regulated sectors sometimes can’t send report data to an outside processor, because source IPs and volumes count as operational data. In that case, self-hosted parsing is the only option. For teams under 10 people on a single provider like Google Workspace or Microsoft 365, a weekly glance at a free digest service is usually enough.
Also remember the blind spots. Not every receiver sends aggregate reports, and many small ISPs and corporate gateways never do. Forensic (ruf) reports are rare in practice because most large providers don’t send them for privacy reasons. Treat aggregate data as a strong sample, not a complete census.
FAQ
How long should I collect DMARC reports before enforcing a policy?
At least two to four weeks at p=none, and longer if you send monthly campaigns or quarterly invoices. A sender that only runs once a month won’t show up in a two-week sample.
Why do my reports show IP addresses I don’t recognize?
Most are forwarders, mailing lists or SaaS tools someone in the company signed up for. Look up the ASN first. Unauthenticated traffic from residential or bulletproof hosting ranges, with no SPF or DKIM pass, is the pattern that points to actual spoofing.
Do I need a separate DMARC record for subdomains?
No. Subdomains inherit the parent policy unless you set sp= or publish a record at _dmarc.sub.example.com. Reports show subdomain traffic under the same organizational domain, so filter by header_from to separate it.
Summary
Aggregate feedback becomes useful once you read it as a comparison. policy_published shows what you asked for, policy_evaluated shows what DMARC concluded and auth_results shows what actually happened. Resolve every source IP to an owner, fix alignment with DKIM rather than piling on SPF includes, and only raise the policy after several weeks of clean data.
One practical habit pays off: keep a simple register of every approved sending service and the date it was added. When a new IP block shows up in next month’s reports, you can check it against the register in seconds instead of spending an afternoon on it.
