# Reading your DMARC reports

> A working guide to DMARC aggregate reports: how to receive rua XML, how to read it, the three findings that matter, and the safe path to p=reject.

Canonical: https://emailfast.dev/blog/reading-dmarc-reports

If you own a domain that sends email, the large mailbox providers are willing to send you a daily report about every message that claimed to come from that domain: where it originated and whether it authenticated. Most domain owners never turn this on. It is free, standardized in [RFC 7489](https://www.rfc-editor.org/rfc/rfc7489), and it is the only routine visibility you will ever get into who is using your name.

## What aggregate reports are

DMARC adds one DNS record to your domain. It tells receivers what to do with unauthenticated mail claiming to be you, and where to send reports about all of it. The aggregate reports — `rua` — arrive roughly daily from each reporting provider as a gzipped XML attachment: counts grouped by sending source, not copies of messages. (The spec also defines `ruf` per-message failure reports; major providers mostly stopped sending those, and you can ignore them.)

## Getting them

Publish a TXT record at `_dmarc.` on your domain:

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

`p=none` is monitor mode: nothing changes about how your mail is treated. Receivers simply start reporting, usually within a day or two. It is the correct first step, and skipping it is how people break their own mail later. One wrinkle: if the reports go to an address on a different domain, that domain must publish an authorization record (`yourdomain.com._report._dmarc.reportingdomain.com TXT "v=DMARC1"`) so reporters know the destination consented.

## Reading one

A report has three parts: metadata (who reported, over what date range), the policy the reporter saw, and `<record>` rows — one per source and result group. Here is a row worth understanding completely:

```xml
<record>
  <row>
    <source_ip>203.0.113.7</source_ip>
    <count>412</count>
    <policy_evaluated>
      <disposition>none</disposition>
      <dkim>fail</dkim>
      <spf>fail</spf>
    </policy_evaluated>
  </row>
  <identifiers>
    <header_from>yourdomain.com</header_from>
  </identifiers>
  <auth_results>
    <spf><domain>mail.crm-vendor.example</domain><result>pass</result></spf>
    <dkim><domain>crm-vendor.example</domain><result>pass</result></dkim>
  </auth_results>
</record>
```

The two result sections disagree on purpose. `auth_results` is raw: did SPF pass for whatever envelope domain was used, did some DKIM signature verify. `policy_evaluated` is what DMARC concluded, and DMARC requires alignment — the domain that passed must match the `From:` domain your reader actually sees. SPF aligns when the envelope-from domain matches; DKIM aligns when the signature's `d=` domain matches. The row above is a service authenticating perfectly — as itself. Everything passes for `crm-vendor.example`, nothing passes for `yourdomain.com`, so DMARC records a double fail. Once you can read that distinction, every report resolves into three stories.

## The three findings that matter

**A legitimate service you forgot.** High counts, a vendor you recognize — CRM, billing, support desk — raw passes for the vendor's own domain, aligned failures. Exactly the row above. The fix is at the vendor: configure it to sign DKIM with your domain (serious senders all support custom-domain DKIM), or align SPF by using your domain in the envelope. This is most of the work on the road to enforcement.

**A forwarder.** Modest counts where SPF fails aligned but your own DKIM passes. Forwarding changes the sending IP and usually the envelope, so SPF breaks; the DKIM signature travels inside the message and survives. This is precisely why DMARC needs only one aligned pass. These rows are normal background — do not chase them to zero. The exception is mailing lists that modify messages (subject tags, footers), which break DKIM too and need per-list decisions.

**Actual spoofing.** Sources you cannot identify, both aligned results failing, sometimes in bursts. Under `p=none` your policy did nothing about them; receivers judged the mail on their own. These rows are your case for tightening.

## The road to p=reject

Enforcement is where DMARC earns its keep, and rushing it is how domains break their own newsletters and invoices. The sequence:

1. Stay at `p=none` until finding one is finished — every legitimate source produces aligned passes across several weeks of clean reports.
2. Move to `p=quarantine`, optionally phased with `pct=`. Keep reading the reports: legitimate mail now landing in spam shows up as aligned failures from sources you recognize.
3. Move to `p=reject`. Exact-domain spoofing now gets refused outright.

Two caveats for the finish line. Subdomains follow the `sp=` tag if you set one, so decide deliberately what `mail.yourdomain.com` and friends should do. And DMARC protects your exact domain only — lookalike domains are a different fight it cannot join.

Give the whole path weeks, not days. The reports are the safety mechanism: each step is taken only when the previous step's reports say it is safe.

One housekeeping note if you send with us: Email Fast ingests your rua reports automatically, as part of [how deliverability works](/features/deliverability).
