ColdOps
All posts
Deliverability

How to Read a DMARC Report (Aggregate XML, Explained)

By Mo Charradi, Founder8 min read

To read a DMARC report, skip most of the XML and answer three questions with it: who is sending as your domain, what share of that mail authenticates, which sources are failing. A DMARC aggregate report is a daily file from a mailbox provider listing every IP address that sent mail claiming to be you, how many messages each one sent, plus what the provider decided about them. Everything else in the file is packaging.

Almost nobody does this. The record gets published on day one with a rua= address, reports start arriving, the folder fills with gzipped XML from Google and Microsoft and a handful of providers you have never heard of, then the whole thing becomes an email filter rule. Which is how domains end up sitting on p=none for two years, watching themselves get spoofed without blocking any of it, unable to tighten because nobody can say what would break.

What actually arrives at your rua address

The rua= tag in your DMARC record is a request for feedback. Providers that honour it send you one report per domain per day, as an XML file attached to an email, usually gzipped and occasionally zipped. Filenames follow a convention that is worth recognising:

google.com!yourdomain.com!1754870400!1754956799.xml.gz

That is the reporting organisation, your domain, then the start and end of the window as seconds since the Unix epoch. So you can sort a folder by filename and get a clean timeline before you open anything.

Two limits to set expectations. These reports are aggregate, meaning they count messages by sending source rather than describing individual emails, so there is no recipient address or subject line in them. And they lag: a report covering Monday typically arrives Tuesday or Wednesday. DMARC also defines a failure report type behind a ruf= tag, but most large providers stopped sending those for privacy reasons, so aggregate reports are the feedback loop you actually get.

Every field in a DMARC report, annotated

The schema is flat and machine generated, which is why it is readable once you know what to ignore. Three blocks matter.

FieldBlockWhat it tells you
<org_name>report_metadataWho sent the report, such as google.com or Enterprise Outlook
<report_id>report_metadataUnique identifier for this report. Use it to dedupe when a provider resends
<begin> / <end>report_metadata > date_rangeThe window covered, in seconds since the Unix epoch. Convert before comparing anything
<domain>policy_publishedThe domain the policy was found on. Confirms which record the provider read
<p>policy_publishedThe policy that provider saw: none, quarantine or reject. Check it against what you believe you published
<sp>policy_publishedThe separate policy for subdomains, if you set one
<source_ip>record > rowThe IP that sent the mail. This is the anchor of the entire report
<count>record > rowMessages that IP sent in the window. Rows are aggregated, so one row can mean one message or forty thousand
<disposition>record > row > policy_evaluatedWhat the provider did: none, quarantine, reject
<dkim>record > row > policy_evaluatedThe DKIM result after alignment. A pass here is a pass DMARC accepted
<spf>record > row > policy_evaluatedThe SPF result after alignment
<header_from>record > identifiersThe From domain the recipient saw. Matters once subdomains start sending
<spf> / <dkim>record > auth_resultsThe raw authentication result before alignment. Looks almost identical, means something different

One trap in that table. Under p=none, disposition reads none on every row, including the ones that failed badly, because you told receivers to take no action. Disposition describes the consequence, not the health. Read the SPF and DKIM verdicts for health.

The distinction that makes reports readable: passing is not aligning

This is the part that trips up everyone reading their first report. It is also the reason a domain can look healthy in one field while DMARC is junking its mail.

SPF and DKIM each authenticate a domain, but not necessarily your domain.

  • SPF checks the envelope sender, the Return-Path address the receiving server sees during the SMTP conversation. On most sending platforms that is the platform's own bounce domain, not yours. SPF passes cleanly for a domain your recipient never sees.
  • DKIM checks the signature's d= value. If your provider signs with a shared domain of their own rather than a key published on yours, DKIM also passes for someone else's domain.

DMARC ignores both of those unless the authenticated domain matches the From address the recipient reads. That match is alignment, the only thing DMARC actually scores. An unaligned pass is a DMARC failure. If your reports show this pattern, the fix is a DKIM key published on your own domain, which the SPF, DKIM and DMARC setup guide walks through per sending domain.

Two rules follow from that, both worth writing down before you summarise anything:

  1. Read policy_evaluated, not auth_results. The auth_results block is the raw outcome. Summarising that block and calling it your DMARC health is the classic way to report a domain as fine while receivers are rejecting its mail.
  2. A message passes DMARC on either aligned SPF or aligned DKIM. Not both. Requiring both is the opposite mistake, the one that makes a perfectly healthy domain look broken. Plenty of legitimate cold email passes on aligned DKIM alone with SPF unaligned forever, which is fine.

How to read a DMARC report in three questions

Everything above is orientation. This is the actual reading, in the same three passes whether you have one report or ninety.

Question 1: who is sending as me

Pull the distinct source_ip values across every report, then put a name to each one. Reverse DNS gets you most of the way, whois covers the rest.

On a well-behaved cold email domain the list is short: your sending platform's IPs, plus Google or Microsoft if the mailboxes live on Workspace or 365. On a company's main domain it is never short, because payroll software, the CRM, the helpdesk and a booking tool are all quietly sending as the domain. That difference is a real argument for keeping outreach on separate sending domains: a short source list is a domain you can actually reason about.

Question 2: what share of it authenticates

Sum count across every row for your total messages seen. Sum count again across rows where either evaluated SPF or evaluated DKIM passed. Divide the second by the first.

The thresholds we use for that ratio:

Pass rateRead it as
99 percent or moreClean. The failures are noise, usually forwarding
95 to 99 percentWatch. Something small is broken or something small is spoofing you
Below 95 percentAct. Tightening the policy today would start junking real mail

Two caveats keep this honest. The rate covers mail the reporting providers saw, so it is a sample weighted toward Gmail and Outlook rather than a full audit of your sending. And zero volume is not a clean bill of health, it is an absence of evidence. A brand new domain with no rows in its reports has proved nothing.

Question 3: which failing sources are mine

Take every row where both evaluated results failed, group by source_ip, sort by count descending. That list is the whole point of the exercise. Each entry on it is one of four things:

  • A legitimate sender of yours that is not aligned. The most common answer by a distance, usually the DKIM problem above. Fixable in DNS.
  • A forwarder. A recipient auto-forwards to another address, SPF breaks in transit because the forwarding server is not in your record, DKIM often survives. Shows up as many IPs each with small counts.
  • Something connected without telling you. A newsletter tool, an invoicing app, a contractor's sequencer. Common on shared domains, rare on dedicated sending domains.
  • Someone spoofing you. Which is what enforcement exists to stop.

The XML cannot tell you which of the four you are looking at. Only recognising the IP can. That is the actual work, which is also the step people skip before deciding their reports look fine.

The decision rule for tightening

Here is the rule the whole exercise builds toward: tighten only when you can name every failing source, not when the pass rate looks good.

The reason is that p=quarantine does not distinguish spoofers from you. It applies to all mail that fails alignment, including your own. Tighten while a legitimate sender of yours is unaligned and you have personally arranged for your campaigns to land in spam, on the domain where you did everything else right. The none versus quarantine decision covers the staged rollout, including the percentage flag that lets you tighten a slice at a time.

In practice that means two to four weeks of reports, every source identified, then p=quarantine. Since the 2024 Google and Yahoo sender requirements made DMARC mandatory for bulk senders, most people already have the record. The reports have been arriving the entire time. Reading a month of them is the cheapest protection available on the domain.

Reading a folder of them without handing them to anyone

Free online DMARC analyzers work by receiving your reports, which means pointing your rua= at their address or forwarding them on. Think about what that hands over. These reports name every IP address that sends on your behalf, dated and counted. For an agency that is a map of your infrastructure and your clients' infrastructure, in one file, refreshed daily.

The format does not justify the trade. It is flat XML with no attributes worth parsing, which is why ColdOps reads these with a small local script rather than a service: point it at a folder of .xml and .gz attachments and it prints the domain, the published policy, messages seen, the authenticated share, then the failing sources worst first with a verdict on whether it is safe to tighten. No account, no upload, nothing leaves the machine. If you would rather not run anything, opening a single report in any text editor and reading the rows by hand is genuinely fine for one domain.

Worth separating two checks that sound the same. The free DMARC checker reads the record you published and tells you what it instructs receivers to do. The aggregate reports tell you what the world actually did with mail claiming to be you. You need both, because a perfect record proves nothing about whether your mail aligns in the wild.

The ongoing problem is that neither check stays true. A regenerated DKIM key, a platform migration, a DNS edit made in a hurry, any of these break alignment on a domain that passed last month. Under an enforced policy the damage is your own campaigns going to spam quietly. ColdOps re-verifies DMARC, SPF, MX and blacklist status across every client sending domain on a schedule, so a record that breaks on Tuesday surfaces as an alert on Tuesday instead of a reply-rate mystery three weeks later.

If your reports have been arriving for a year and you have never opened one, start with the most recent week. Name the sources, work out the pass rate, then decide whether p=none is still doing anything for you.

Frequently asked

How do I read a DMARC report?
Ignore most of the XML and answer three questions from it: which IP addresses are sending as your domain, what share of that mail authenticates, which sources are failing. The answers live in the record rows, where each row gives a source IP, a message count, then the SPF and DKIM results after alignment. Read the results inside policy_evaluated, not the ones inside auth_results.
What does a DMARC aggregate report contain?
Three things: metadata naming the reporting organisation and the time window it covers, a copy of the DMARC policy that organisation found published on your domain, then one row per sending source. Each row carries the source IP, how many messages it sent in the window, what the provider did with them, then whether SPF and DKIM passed with alignment. It does not contain message content or recipient addresses.
Why does my DMARC report show SPF pass but DMARC fail?
Because passing is not the same as aligning. SPF authenticates the envelope sender domain, which on most sending platforms is the platform's own bounce domain rather than yours, so SPF passes for a domain the recipient never sees. DMARC only counts a pass when the authenticated domain matches the From address, so an unaligned SPF pass fails DMARC.
What is a good DMARC pass rate?
Above 99 percent of observed messages is a clean domain, 95 to 99 percent is worth investigating before you change anything, while below 95 percent means a meaningful share of your mail would be junked if you tightened the policy today. The rate only covers mail the reporting providers saw, so it is a sample rather than a complete audit of everything you sent.
Do I need a DMARC report analyzer?
No. The format is simple enough that a small script or a free viewer will summarise a folder of reports fine. The consideration worth weighing is privacy: the reports name every IP address that sends on your behalf, which is a map of your sending infrastructure, so think about who receives that map before you point your rua address at a third party.

Keep reading