DMARC Aggregate Reports: How to Read Them and What They Reveal
DMARC aggregate reports show who sends email as your domain and whether it passes authentication. Learn how to read RUA reports and act on the data inside them.
What are DMARC aggregate reports?
When you publish a DMARC record, you can include a rua (Reporting URI for Aggregate reports) tag that tells receiving mail servers to send you reports about email they receive claiming to be from your domain:
_dmarc.yourcompany.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@yourcompany.com"
Every major email provider -- Gmail, Microsoft 365, Yahoo, Apple -- sends these reports. They arrive as XML files (usually compressed as .gz or .zip) once per day and contain data about every email that claimed your domain in the From: header.
These reports are the foundation of a safe DMARC deployment. Without them, you are flying blind when tightening your policy from p=none to p=quarantine to p=reject.
What is inside a DMARC aggregate report?
Each report covers a time period (usually 24 hours) and contains rows of data. Each row represents a group of emails from a specific source IP with identical authentication results. The key fields:
Source IP and volume
Which IP address sent the email and how many messages:
<row>
<source_ip>203.0.113.25</source_ip>
<count>1524</count>
</row>
Policy evaluated
What the receiving server did with the email based on your DMARC policy:
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>fail</spf>
</policy_evaluated>
The disposition field shows the action taken: none (delivered normally), quarantine (sent to spam), or reject (bounced). The dkim and spf fields show the DMARC alignment result (not just the raw SPF/DKIM check).
Authentication results
Detailed SPF and DKIM results:
<auth_results>
<dkim>
<domain>yourcompany.com</domain>
<result>pass</result>
<selector>selector1</selector>
</dkim>
<spf>
<domain>yourcompany.com</domain>
<result>pass</result>
</spf>
</auth_results>
Reading raw DMARC XML
The raw XML can be daunting. Here is a minimal example:
<?xml version="1.0" encoding="UTF-8"?>
<feedback>
<report_metadata>
<org_name>google.com</org_name>
<date_range>
<begin>1716768000</begin>
<end>1716854400</end>
</date_range>
</report_metadata>
<policy_published>
<domain>yourcompany.com</domain>
<p>none</p>
</policy_published>
<record>
<row>
<source_ip>203.0.113.25</source_ip>
<count>450</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>yourcompany.com</header_from>
</identifiers>
<auth_results>
<dkim>
<domain>yourcompany.com</domain>
<result>pass</result>
</dkim>
<spf>
<domain>yourcompany.com</domain>
<result>pass</result>
</spf>
</auth_results>
</record>
</feedback>
This tells you: Google received 450 emails from IP 203.0.113.25 claiming to be from yourcompany.com. Both SPF and DKIM passed and were aligned. The disposition was none (delivered normally), consistent with the p=none policy.
Parsing reports from the command line
Extract and read compressed reports
# Reports typically arrive as .gz or .zip
gunzip report.xml.gz
# or
unzip report.zip
# Pretty-print the XML
xmllint --format report.xml | less
Quick summary with command-line tools
# Extract source IPs and counts
xmllint --xpath '//row' report.xml | \
grep -oP '<source_ip>\K[^<]+|<count>\K[^<]+'
Using a simple Python script
import xml.etree.ElementTree as ET
import sys
tree = ET.parse(sys.argv[1])
root = tree.getroot()
for record in root.findall('.//record'):
ip = record.find('.//source_ip').text
count = record.find('.//count').text
dkim = record.find('.//policy_evaluated/dkim').text
spf = record.find('.//policy_evaluated/spf').text
disposition = record.find('.//policy_evaluated/disposition').text
print(f"IP: {ip:20s} Count: {count:6s} DKIM: {dkim:5s} SPF: {spf:5s} Action: {disposition}")
Free DMARC report analysis tools
Parsing XML manually works for occasional checks, but for ongoing monitoring you need a tool that aggregates reports over time:
- DMARC Analyzer -- freemium, web-based dashboard
- Postmark DMARC -- free weekly digests, very beginner-friendly
- URIports -- free tier, real-time report processing
- parsedmarc -- open-source, self-hosted, outputs to Elasticsearch/Splunk
To use any of these, point your rua tag at their processing address instead of (or in addition to) your own mailbox:
_dmarc.yourcompany.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@yourcompany.com,mailto:your-id@ag.dmarcanalyzer.com"
What to look for in DMARC reports
Legitimate senders failing authentication
This is the most important thing to identify before tightening your DMARC policy. Look for:
- Your own mail servers failing SPF because the sending IP is not in your SPF record
- Third-party services (marketing tools, CRMs, ticketing systems) sending as your domain without proper DKIM signing
- Forwarded email failing SPF (this is normal -- email forwarding breaks SPF because the forwarding server's IP is not in your SPF record)
Fix each of these before moving from p=none to p=quarantine.
Unknown senders passing authentication
If an IP address you do not recognise is sending email as your domain and passing SPF or DKIM, investigate:
- Is it a third-party service a team member set up without telling IT?
- Is it a partner or vendor sending on your behalf?
- Is it an attacker who has compromised your SPF record or DKIM key?
High volumes of failing email
Large numbers of emails failing both SPF and DKIM from IPs you do not recognise are likely spoofing attempts. This is exactly what DMARC is designed to catch. When you move to p=reject, these will be blocked.
Forwarding services
Email forwarding (mailing lists, alumni addresses, personal forwarding rules) causes SPF failures because the forwarding server's IP is not in your SPF record. DKIM usually still passes because the signature travels with the message. This is why DKIM is critical -- it survives forwarding while SPF does not.
The path from p=none to p=reject
DMARC aggregate reports enable a safe, gradual policy rollout:
Phase 1: p=none (monitoring only)
_dmarc.yourcompany.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@yourcompany.com"
Collect reports for at least 2--4 weeks. Identify all legitimate senders and fix authentication issues.
Phase 2: p=quarantine with pct
_dmarc.yourcompany.com. IN TXT "v=DMARC1; p=quarantine; pct=10; rua=mailto:dmarc@yourcompany.com"
Start by quarantining only 10% of failing email. Monitor reports for problems. Gradually increase pct to 25, 50, 100.
Phase 3: p=reject
_dmarc.yourcompany.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@yourcompany.com"
Full protection. Unauthorised email claiming your domain is rejected. Continue monitoring reports to catch any newly added services that need authentication.
For the full policy explanation, see our DMARC policy guide. For the broader email authentication picture, see email spoofing prevention.
How SurfaceScan helps
SurfaceScan monitors your DMARC configuration and processes your aggregate reports to surface actionable insights. The Email Security dashboard shows which IPs are sending as your domain, whether they pass authentication, and which legitimate senders still need to be configured. SurfaceScan flags domains stuck on p=none for too long and guides you through the rollout to p=reject, with clear visibility into what would break at each stage.
Related articles
DMARC Policy: From None to Quarantine to Reject
DMARC ties SPF and DKIM together. Learn the three policy levels (none, quarantine, reject) and how to implement DMARC safely without breaking email.
Email Spoofing Prevention: The Complete SPF, DKIM, and DMARC Guide
Prevent email spoofing with SPF, DKIM, and DMARC working together. Step-by-step implementation guide for IT admins to fully protect their domain from impersonation.
SPF Records: What They Are and How to Fix Them
SPF tells receiving mail servers which servers are allowed to send email for your domain. Learn softfail vs hardfail and fix common SPF mistakes.