Understanding DMARC Reports

A DMARC aggregate report is what receivers send back to you to describe what they saw from your domain. The information is comprehensive. The format is unfriendly. Here is what is in one and how to read it.

What An Aggregate Report Contains

Every report covers one of your domains over a fixed time window — usually 24 hours. It enumerates the source IPs that sent mail claiming to be from your domain, how many messages each sent, and the SPF and DKIM results for each.

The report contains three structural blocks:

  • report_metadata — who sent the report (Google, Microsoft, Yahoo, Apple, and many others), the unique report ID, and the time window covered.
  • policy_published — a snapshot of your DMARC record as the receiver fetched it. Useful for confirming a recent record change actually propagated.
  • record — one block per source IP. Includes the message count, the receiver's evaluated disposition, the alignment results, and the auth-results for SPF and DKIM separately.

Cadence And Sources

Reports arrive by email as gzipped XML attachments. The filename follows a convention: <reporter>!<your-domain>!<begin>!<end>.xml.gz. The body of the email is incidental — the data lives in the attachment.

Major reporters and their typical cadence:

  • Google — daily, for every domain it sees mail from.
  • Microsoft — daily.
  • Yahoo — daily.
  • Apple (iCloud) — daily.
  • Smaller receivers — daily to weekly. Many do not report at all.

A high-volume domain can expect dozens of reports per day. Most are short — five to twenty source IPs. A domain under spoofing pressure can produce reports with hundreds of source IPs and tens of thousands of messages counted.

The XML Structure

An aggregate report is a single <feedback> XML document. Here is a representative example with two source IPs — one legitimate, one spoofing — for the domain example.com.

<?xml version="1.0" encoding="UTF-8" ?>
<feedback>
  <report_metadata>
    <org_name>google.com</org_name>
    <email>noreply-dmarc-support@google.com</email>
    <report_id>1234567890987654321</report_id>
    <date_range>
      <begin>1717459200</begin>
      <end>1717545600</end>
    </date_range>
  </report_metadata>

  <policy_published>
    <domain>example.com</domain>
    <adkim>r</adkim>
    <aspf>r</aspf>
    <p>quarantine</p>
    <sp>quarantine</sp>
    <pct>100</pct>
  </policy_published>

  <record>
    <row>
      <source_ip>209.85.220.41</source_ip>
      <count>187</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>
        <selector>s1</selector>
        <result>pass</result>
      </dkim>
      <spf>
        <domain>example.com</domain>
        <result>pass</result>
      </spf>
    </auth_results>
  </record>

  <record>
    <row>
      <source_ip>198.51.100.13</source_ip>
      <count>3</count>
      <policy_evaluated>
        <disposition>quarantine</disposition>
        <dkim>fail</dkim>
        <spf>fail</spf>
      </policy_evaluated>
    </row>
    <identifiers>
      <header_from>example.com</header_from>
    </identifiers>
    <auth_results>
      <spf>
        <domain>marketing-bot.example.net</domain>
        <result>fail</result>
      </spf>
    </auth_results>
  </record>
</feedback>

Reading The Example

The First Record — A Legitimate Sender

  • source_ip 209.85.220.41 — a Google sending IP.
  • count 187 — Google saw 187 messages from this IP claiming example.com over the report window.
  • policy_evaluated — Google's verdict: dkim=pass and spf=pass, so disposition=none (the message was delivered normally). This row is healthy.
  • auth_results — the actual SPF and DKIM verification. Both passed and both aligned (the DKIM signing domain and the SPF return-path domain are example.com, which matches the header_from).

The Second Record — A Spoofing Attempt

  • source_ip 198.51.100.13 — an unfamiliar IP.
  • count 3 — three messages over the window. Small volume, but the From header claimed your domain.
  • policy_evaluated — both dkim=fail and spf=fail. The receiver applied your p=quarantine policy and sent the message to the spam folder. If you were at p=reject, it would have been refused at SMTP.
  • auth_results — the SPF check used marketing-bot.example.net as the envelope-sender domain. That domain does not match the header_from, so SPF could not align even if it had passed. This is exactly what spoofing looks like in the report.

What To Do With It

Read the rows where the verdict is fail. For each, ask:

  • Is the source_ip familiar? If yes, it is a misconfigured authorized sender. Add it to SPF or get DKIM signing in place.
  • Is the source_ip unfamiliar? If the message volume is low and persistent, you are being impersonated. The policy is doing its job.
  • Is the policy_evaluated disposition none? Then you are still at p=none and the spoofer is getting through. Reading reports is the path to changing that.

Skip The XML

Auspex DMARC turns every aggregate report into a plain-English summary delivered to your inbox.