Not receiving DMARC reports: read the record first
DMARC aggregate reports flow because a domain publishes a rua= tag in its _dmarc TXT record, and receiving mail servers send a daily XML summary to that address. When you are not receiving DMARC reports, the cause is almost always one of: the rua= URI is malformed, the report mailbox lives on a different domain and the required authorization record is missing, there is no mail volume to report on, or the receiving mailbox is rejecting the reports. Work through them in that order.
Start by reading the record exactly as the world sees it. DMARC is published at the _dmarc label of the visible From: domain (RFC 9989 section 4):
dig +short TXT _dmarc.example.com
dig +short TXT _dmarc.example.com @1.1.1.1
dig +short TXT _dmarc.example.com @8.8.8.8
The @1.1.1.1 and @8.8.8.8 arguments query Cloudflare and Google directly so you see the published value rather than a stale local cache. You should get back exactly one record beginning with v=DMARC1 that contains a rua= tag.
Cause 1: the rua URI is malformed
The rua= value is a comma-separated list of DMARC URIs, and each one must be a full URI with the mailto: scheme (RFC 9989 section 4.7). A missing scheme or the wrong separator silently breaks report delivery. These are the failures that look right but are not:
# Wrong - missing the mailto: scheme
v=DMARC1; p=none; [email protected]
# Wrong - comma used as the tag separator instead of semicolon
v=DMARC1, p=none, rua=mailto:[email protected]
# Wrong - semicolon between two report addresses (should be a comma)
v=DMARC1; p=none; rua=mailto:[email protected];mailto:[email protected]
# Correct - mailto: on every address, addresses comma-separated, tags semicolon-separated
v=DMARC1; p=none; rua=mailto:[email protected],mailto:[email protected]
The separator rule is the one people get wrong most often: tags are separated by semicolons, but multiple addresses inside a single rua= are separated by commas. Check for smart quotes pasted from a document, trailing spaces, and a DNS provider that wrapped the value across fields. Regenerate a clean record with the DMARC record generator if you are unsure of the syntax.
Cause 2: missing external authorization record
This is the cause most people miss. If your rua= mailbox is on a domain whose organizational domain differs from the domain that published the DMARC policy, the receiving mail server will not send reports until the external domain publishes a record authorizing it. This is the external destination verification defined in RFC 9990 section 4, and it exists so that a domain cannot name your mailbox as a report sink without your consent.
Concretely: if example.com publishes rua=mailto:[email protected], the organizational domains differ (example.com versus dmarctrust.com), so the receiver constructs and queries a verification record. The query name is built by prepending the literal string _report._dmarc to the external domain, then prepending the policy domain (RFC 9990 section 4):
<policy-domain>._report._dmarc.<external-domain>
Worked through with the example above, the receiver issues this lookup:
dig +short TXT example.com._report._dmarc.reports.dmarctrust.com @1.1.1.1
The external domain must answer with a TXT record that parses as DMARC tag-value pairs, with v=DMARC1 mandatory and first. The minimum valid content is just the version tag. So reports.dmarctrust.com would publish:
example.com._report._dmarc.reports.dmarctrust.com. IN TXT "v=DMARC1"
RFC 9990 section 4 is explicit about the consequence of the record being absent or unparseable: "Where the above algorithm fails to confirm that the external reporting was authorized by the Report Consumer, the URI MUST be ignored by the Mail Receiver generating the report." In other words, no record, no report. A domain that is willing to receive reports for any policy domain can publish a wildcard instead:
*._report._dmarc.example.com. IN TXT "v=DMARC1"
If your reporting address is a hosted DMARC service, the provider tells you the exact authorization record to publish. With DMARCTrust your reporting address sits on a domain we control, so this authorization is handled for you; the record above is what to check when you point rua= at a mailbox on a domain you own but separate from the policy domain.
Cause 3: there is no mail to report on
Aggregate reports summarize messages a receiver evaluated against your DMARC policy. If the domain sends little or no mail to a given provider, that provider has nothing to summarize and sends no report. This is the normal state for parked domains, freshly registered domains, and domains used only for inbound mail.
Before assuming the record is broken, confirm the domain is actually sending mail that reaches reporting receivers. A domain that sends a handful of messages per month to small mail hosts may legitimately receive zero aggregate reports, because the receivers that got the mail do not generate them.
Cause 4: the report mailbox is bouncing or rejecting
Reports are delivered by email, so the report mailbox has to accept them. Aggregate reports are gzip or zip attachments and can be large for a high-volume domain. A mailbox that is over quota, that rejects attachments, or that has a size limit below the report size will bounce the report, and the sending receiver does not retry indefinitely.
Check that the rua= mailbox exists and accepts mail with attachments. If you control it, look at its inbound logs for messages from reporting senders that were rejected for size or quota. A dedicated mailbox or a hosted DMARC endpoint avoids this, because it is sized for report ingestion.
Cause 5: cadence and coverage expectations
Two expectations trip people up after they have ruled out configuration problems.
-
Cadence is daily. Under DMARCbis, receivers SHOULD send aggregate reports once per day (RFC 9990 section 3.1.4, RFC 9989 section 8). The configurable
ri=interval tag from RFC 7489 was removed, so daily is the cadence you get. A record published this morning typically produces its first report the next day, not within minutes. - Coverage is partial. Sending aggregate reports is voluntary. Google, Yahoo, and Microsoft send them; many smaller receivers send none. Getting reports from only a few sources is expected, not a fault.
-
Forensic (ruf) reports are rare. A
ruf=tag requests failure reports with message-level detail. Most major providers do not send them at all, largely for privacy reasons, so an empty forensic feed is normal. Rely on aggregate data for your day-to-day view.
Validate the fix
After you correct the record or publish the authorization record, wait at least one TTL, then re-check the live values from public resolvers:
dig +short TXT _dmarc.example.com @1.1.1.1
dig +short TXT example.com._report._dmarc.reports.dmarctrust.com @1.1.1.1
You want exactly one valid v=DMARC1 policy record with a well-formed rua=, and, if your report mailbox is on a separate domain, a matching authorization record that returns v=DMARC1. Use the DMARCTrust DMARC checker to confirm the published policy parses cleanly, then allow up to 48 hours for the first reports to arrive.
Once reports start flowing, the raw aggregate XML is hard to read by hand. The DMARC reports dashboard turns the daily XML into a per-source view that shows the failing IP, the evaluated identity, and which alignment path to fix. If you are not yet collecting reports anywhere, create an account to get a hosted reporting address with the external authorization handled for you.
Related fixes
- If your record is still in monitoring mode, read DMARC policy not enabled.
- If lookups time out or return SERVFAIL intermittently, read DMARC temperror and DNS timeouts.
- If a DMARC lookup finds nothing at all, read no DMARC record found.
- If reports show passes but spoofing continues, read DMARC pass but spoofing continues.
- If reports show your ESP failing alignment, read ESP mail fails DMARC.