If you’ve turned on DMARC for a domain and asked mailbox providers to send you reports, you’ll have started receiving emails like google.com!example.com!1746057600!1746144000.xml.gz with cryptic XML attachments. Those are DMARC aggregate reports — RUA reports, for the rua= tag that triggers them — and they’re how the email ecosystem tells you who’s sending mail claiming to be from your domain and whether their authentication is passing.
The reports look intimidating the first time you open one. They’re not. Each report tells a simple story: “in this 24-hour window, here are the IP addresses that sent mail with your domain in the From header, here’s how many messages each one sent, here’s whether SPF and DKIM aligned, and here’s what we did with them based on your published policy.”
This article walks through the structure of a DMARC aggregate report, explains what each field means, shows you what to look for, and covers the practical question of when you should move your DMARC policy from p=none to p=quarantine to p=reject.
What a DMARC aggregate report actually is
DMARC is a policy layer that sits on top of SPF and DKIM. When you publish a DMARC record at _dmarc.example.com, you’re telling receivers two things:
- What to do with mail that fails authentication (
p=none,p=quarantine, orp=reject) - Where to send reports about that mail (
rua=mailto:reports@example.com)
Receivers — Google, Microsoft, Yahoo, ProtonMail, and most major mailbox providers — generate a report for each domain that authorised them via rua=, typically once every 24 hours. The report is an XML file, usually gzip-compressed, summarising every message stream they saw claiming to be from your domain in that window.
Crucially, aggregate reports don’t contain message bodies, subjects, or recipients. They’re a statistical summary: source IP, message count, authentication results, and the disposition applied. They’re a deliverability monitoring tool, not a forensic one.
The structure of a DMARC report
Every aggregate report has three top-level sections, then a series of <record> blocks. Here’s the skeleton:
<?xml version="1.0" encoding="UTF-8"?>
<feedback>
<report_metadata>
<!-- Who generated the report and when -->
</report_metadata>
<policy_published>
<!-- The DMARC policy that was in effect for your domain -->
</policy_published>
<record>
<!-- One source IP and what happened with its messages -->
</record>
<record>
<!-- The next source IP, and so on -->
</record>
</feedback>
Each <record> summarises one source — typically one IP address — and the authentication outcome for all messages from that source during the report window.
Section 1: <report_metadata>
This block tells you who generated the report and which time window it covers. A typical example:
<report_metadata>
<org_name>google.com</org_name>
<email>noreply-dmarc-support@google.com</email>
<report_id>1234567890123456789</report_id>
<date_range>
<begin>1746057600</begin>
<end>1746144000</end>
</date_range>
</report_metadata>
The fields are mostly self-explanatory:
<org_name>— the receiver that generated the report. Common values includegoogle.com,Yahoo,Enterprise Outlook,Outlook.com, and various corporate filter vendors.<email>— the sender of the report email.<report_id>— the receiver’s internal identifier for this report. Useful if you need to ask them about it.<date_range>— start and end of the report window in Unix epoch seconds. Most receivers use a 24-hour window aligned to UTC midnight.
The window length matters when you’re investigating. If you see a spike of failures on a specific day, the start/end timestamps tell you exactly which day of activity the report covers.
Section 2: <policy_published>
This block confirms what DMARC policy the receiver saw when it processed your mail. Important because receivers cache DNS lookups, so the policy a report references might lag behind a recent change you’ve made.
<policy_published>
<domain>example.com</domain>
<adkim>r</adkim>
<aspf>r</aspf>
<p>quarantine</p>
<sp>quarantine</sp>
<pct>100</pct>
</policy_published>
Field by field:
<domain>— your domain.<adkim>and<aspf>— alignment mode for DKIM and SPF.ris relaxed (organisational domain match —mail.example.comaligns withexample.com),sis strict (exact domain match required).<p>— the policy for the organisational domain. One ofnone,quarantine, orreject.<sp>— the policy for subdomains (if you’ve set a different one).<pct>— the percentage of failing messages the receiver should apply the policy to.100means “apply the policy to all failures”; lower values are used during gradual rollouts.
If the <p> value in a report disagrees with what you’ve published right now, your DNS change hasn’t propagated to that receiver yet, or you changed the policy mid-window.
Section 3: <record> — the part that matters
Each <record> represents one source IP and what happened to its messages. This is where you find out who’s actually sending mail as your domain. A complete record looks like this:
<record>
<row>
<source_ip>209.85.220.41</source_ip>
<count>43</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<domain>example.com</domain>
<result>pass</result>
<selector>google</selector>
</dkim>
<spf>
<domain>example.com</domain>
<result>pass</result>
</spf>
</auth_results>
</record>
Breaking this down:
<row> — the headline numbers
<source_ip>— the IP that delivered the messages. Resolve it to find out who’s behind it (Google, your ESP, your own mail server, an attacker, or a forwarder).<count>— how many messages came from this IP in the window.<policy_evaluated>— what DMARC decided overall. The<disposition>is what the receiver actually did:none(delivered normally),quarantine(sent to spam/junk), orreject(refused at SMTP). The<dkim>and<spf>values here are the DMARC-aligned results — whether the passing SPF/DKIM authentication used a domain that aligned with the From header, not just whether SPF/DKIM passed in general.
<identifiers>
<header_from>— the domain in the From header that the receiver actually saw. Should be your domain.
<auth_results>
The raw SPF and DKIM authentication results before DMARC alignment is considered. This is where you diagnose why something failed.
For DKIM, each signature that was attempted on the message gets its own <dkim> block, with the signing <domain>, the <selector>, and the <result> (pass, fail, none). A message can have multiple DKIM signatures.
For SPF, the <domain> is the envelope sender domain (the MAIL FROM, not the header From), and <result> is the raw SPF result.
The two together tell you the full story. Consider this contrast:
A passing record:
<row><source_ip>209.85.220.41</source_ip><count>43</count>
<policy_evaluated>
<disposition>none</disposition><dkim>pass</dkim><spf>pass</spf>
</policy_evaluated>
</row>
43 messages from a Google IP, SPF and DKIM both aligned, delivered normally. Nothing to do.
A failing record:
<row><source_ip>185.220.101.7</source_ip><count>112</count>
<policy_evaluated>
<disposition>reject</disposition><dkim>fail</dkim><spf>fail</spf>
</policy_evaluated>
</row>
112 messages from an unknown IP, neither SPF nor DKIM aligned, rejected by your p=reject policy. This is what DMARC is for — a forgery attempt that was blocked.
What to actually look for in a report
Most DMARC reports are boring. The vast majority of records will show your legitimate sending IPs (your mail server, your ESP, your marketing platform) with dkim: pass and spf: pass and a disposition of none. That’s the expected state.
When you scan a report, look for these three things:
1. Records where DMARC failed (both DKIM and SPF show fail)
These fall into three categories:
- Outright forgery — an attacker spoofing your domain. The source IP won’t be one of your senders or your providers. Common in real-world reports for any well-known domain.
- A legitimate sender you forgot to authorise — a new SaaS tool sending on your behalf, a forgotten internal script, a marketing platform someone signed up for. This is exactly what
p=noneis designed to catch before you tighten policy. - Forwarding — when someone forwards your mail through a personal account or mailing list, the forwarder’s IP appears as the source, and SPF fails because that IP isn’t in your record. DKIM usually survives forwarding (the signature is in the headers, which forwarders typically preserve), so a record with
dkim: passandspf: failis often a forwarder, not a problem.
2. Records where alignment failed but raw auth passed
This is subtler. If you see <auth_results> showing spf: pass but <policy_evaluated> showing spf: fail, it means SPF technically passed but on a different domain than the From header — i.e. the alignment failed. Common when third-party senders use their own envelope domain for bounce handling.
The same applies to DKIM: a signature can pass but be from a different signing domain (d=mailer.com) than your From header (example.com), failing alignment.
These are usually fixable by configuring the third-party sender to use a custom return-path domain (for SPF alignment) or to sign with your domain (for DKIM alignment). Most reputable ESPs support both.
3. Volume anomalies
A sudden spike of mail from a source you don’t recognise is worth investigating, even if it’s passing authentication. If your typical daily volume from “your-marketing-platform” is 5,000 messages and one report shows 50,000, something’s changed — either legitimate (a campaign) or a compromised account.
The policy progression: none → quarantine → reject
DMARC’s biggest value is policy enforcement, but moving too fast breaks legitimate mail. The standard progression most deliverability practitioners follow:
Phase 1: p=none — pure monitoring. Receivers send you reports but apply no policy. Run this for at least 4–6 weeks, ideally longer, until you’ve identified every legitimate sending source and confirmed they all align. The reports tell you what’s missing from your authentication setup.
Phase 2: p=quarantine with pct= ramping up — start by enforcing quarantine on a small percentage of failing mail (pct=10), watch reports for unexpected drops in legitimate volume, then increase to pct=25, pct=50, pct=100. Each step typically runs for 1–2 weeks. The percentage parameter is a safety valve: even if you’ve missed a legitimate sender, only that fraction of their messages get quarantined.
Phase 3: p=reject — once p=quarantine; pct=100 has run cleanly for several weeks with no unexpected failures, move to reject. From this point, anything failing DMARC is bounced at SMTP and never reaches the recipient.
The whole progression is typically 2–6 months, depending on how complex your sending infrastructure is. Rushing it causes outages; skipping it entirely is fine for parked domains but risky for active ones.
Limitations of aggregate reports
Aggregate reports are great at the macro view but have real blind spots:
- No message content. You can see that an IP sent 50 failing messages, but not what they said, who they were to, or what subject lines they used. For forensics on a specific phishing campaign, you need the
ruf=(failure) reports — which fewer receivers send, due to privacy concerns about including message details. - Latency. Reports arrive 24 hours after the events. They’re useless for real-time detection.
- Sampling. Some receivers only report on a subset of mail, particularly for high-volume domains. If a receiver reports 100 messages from an IP, that may not be the full count.
- Inconsistent vendor support. Most major mailbox providers send reports. Some corporate filters don’t. Treat absent data as absence of that receiver’s data, not absence of mail.
- No actionability for forwarded mail. Forwarders breaking SPF is structural — DMARC can’t fix it. Mitigation lives in DKIM, ARC, and accepting some level of forwarder failure as background noise.
The bottom line
A DMARC aggregate report is a 24-hour summary of every IP that sent mail claiming to be from your domain, with the authentication results for each. Most of what you’ll see is your own legitimate mail flowing through. The valuable signal is the small fraction that fails — that’s either forgery to block, a legitimate sender you need to authorise, or a forwarder whose behaviour you can’t change.
Read reports regularly while you’re in p=none. Look for unfamiliar source IPs, alignment failures on known senders, and volume anomalies. When the report data is clean for several weeks, tighten policy in stages. The reports keep coming whether you read them or not — the question is whether you’re using them to actually improve your domain’s security posture.