Skip to main content
email·digit

How to read a DMARC report

A DMARC aggregate report is an XML file, sent by each mailbox provider that received mail using your domain, that lists every sending IP, how many messages it sent, and whether they passed SPF, DKIM and DMARC. To read one, look at each <record>: the IP, the count, the aligned verdict in policy_evaluated, and the raw checks in auth_results.

What an aggregate report is

When your DMARC record has an rua tag, receivers send a summary of the mail they saw claiming to be from your domain to that address. It contains no message content, only counts and authentication results grouped by sending IP. These are the aggregate (RUA) reports. Failure reports (RUF) are a separate, rarer format and are not covered here.

A typical record that asks for reports looks like this:

_dmarc.example.com.  TXT  "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

Who sends it, how often, and why it is compressed

  • Who: every receiving server that supports DMARC reporting and got mail using your domain. If you send to many providers, you get many reports, one per provider per period.
  • How often: usually once a day. The original DMARC specification set the default interval at 86,400 seconds, which is 24 hours.
  • Why compressed: the specification says the XML file should be compressed with gzip, so most reports arrive as an attachment ending in .xml.gz. Extract it to get the XML.

An annotated sample report

This is a shortened report from a receiver at example.net about mail using example.com. Comments explain each part.

<feedback>
  <report_metadata>
    <org_name>example.net</org_name>          <!-- who sent the report -->
    <email>noreply-dmarc@example.net</email>
    <report_id>1234567890</report_id>
    <date_range>
      <begin>1759708800</begin>               <!-- Unix time, start of period -->
      <end>1759795199</end>                   <!-- Unix time, end of period -->
    </date_range>
  </report_metadata>

  <policy_published>                          <!-- your DMARC record, as they saw it -->
    <domain>example.com</domain>
    <adkim>r</adkim>                          <!-- DKIM alignment: r relaxed, s strict -->
    <aspf>r</aspf>                            <!-- SPF alignment: r relaxed, s strict -->
    <p>none</p>                               <!-- policy for example.com -->
    <sp>none</sp>                             <!-- policy for its subdomains -->
    <pct>100</pct>                            <!-- share of failing mail the policy covers -->
  </policy_published>

  <record>
    <row>
      <source_ip>192.0.2.10</source_ip>       <!-- the server that sent the mail -->
      <count>842</count>                      <!-- messages from that IP in the period -->
      <policy_evaluated>                      <!-- the DMARC verdict, after alignment -->
        <disposition>none</disposition>       <!-- what the receiver did: none, quarantine, reject -->
        <dkim>pass</dkim>                     <!-- DKIM passed AND aligned with header_from -->
        <spf>fail</spf>                       <!-- SPF did not pass aligned -->
      </policy_evaluated>
    </row>
    <identifiers>
      <header_from>example.com</header_from>  <!-- the From: domain people see -->
      <envelope_from>bounce.example.net</envelope_from> <!-- the Return-Path domain -->
    </identifiers>
    <auth_results>                            <!-- the raw checks, before alignment -->
      <dkim>
        <domain>example.com</domain>          <!-- the d= domain in the signature -->
        <selector>s1</selector>               <!-- the key name used -->
        <result>pass</result>
      </dkim>
      <spf>
        <domain>bounce.example.net</domain>   <!-- the domain SPF checked -->
        <result>pass</result>
      </spf>
    </auth_results>
  </record>
</feedback>

A real report has one <record> per sending IP and result combination, so a busy domain can have dozens.

policy_evaluated vs auth_results

This is the part that confuses most people, because the same record can say SPF passed and SPF failed. It is not a contradiction. The two sections answer different questions.

SectionQuestion it answersIn the sample
auth_resultsDid the check pass for the domain it checked?SPF passed for bounce.example.net
policy_evaluatedDid it pass for a domain that matches the From: domain?SPF fails, because bounce.example.net is not example.com

DMARC passes when at least one of SPF or DKIM passes and is aligned. In the sample, DKIM was signed with d=example.com, which matches the From: domain, so the message passes DMARC even though SPF is not aligned. If you want the full explanation of alignment, read SPF and DKIM pass but DMARC fails.

You may also see a <reason> inside policy_evaluated, with a type such as forwarded, mailing_list or local_policy. That means the receiver applied something other than your published policy, and says why.

Patterns worth recognizing

  • DKIM pass, SPF fail, both identifiers your domain: usually forwarding. The forwarding server is not in your SPF record, but the signature survived.
  • SPF pass in auth_results, fail in policy_evaluated: the sending service uses its own bounce domain. SPF passed for that domain, not yours.
  • DKIM pass for a domain that is not yours: the service signs with its own domain. Set up DKIM for your domain in its settings.
  • Both fail, from an IP you do not know: a forgotten sender, a misconfigured server or a spoofer. Look up who owns the IP before you decide.

Newer reports look slightly different

DMARC was updated in 2026: RFC 9989 replaced RFC 7489, and the aggregate report format now has its own document, RFC 9990. The update drops pct in favor of a testing flag (t), and adds np, a policy for subdomains that do not exist. Reports from receivers that have adopted it may show <testing> and <np> in policy_published. The rows, identifiers and auth results read the same way.

What to do next

  1. List every source IP. Group them by who owns them. Most should be services you already use to send mail.
  2. Check the ones that fail. A known service failing alignment needs SPF or DKIM set up on your domain. An IP you do not recognize needs investigating: see unknown IPs in your DMARC report.
  3. Stay on p=none until your own mail passes. Once every legitimate source passes aligned, move to p=quarantine, then p=reject.
  4. Keep reading reports after you tighten the policy. A new tool someone adds next month will show up here first.

Reading a week of reports by hand gets slow. Our free DMARC report analyser takes the XML, pasted or as an uploaded .xml file (extract the .gz first), and explains it in plain English: who is sending as you, what passed and what failed alignment. No account and no cap. If you would rather not do this at all, Email Digit manages authentication for domains you send from: you publish four DNS records once, then rotate keys in one click and step up DMARC when your reports say it is safe, without editing DNS again.