Blog · Dmarc

Why Your DMARC Aggregate Reports May Not Be RFC 7489 Compliant (And What to Do About It)

Your DMARC aggregate reports might be broken. Not in an obvious way - no error message, no warning from your vendor. But quietly, in a way that means you're missing data you set up DMARC to collect. Many receivers generate reports that don't fully comply with RFC 7489, and most parsing tools either silently skip the broken records or fail to parse the report at all. You think you have visibility. You actually have partial visibility.

The IETF maintains an errata list for RFC 7489 with dozens of filed issues. Practitioners have been reporting non-compliant DMARC reports in working groups and forums for years. Here's what actually goes wrong, with specific examples.

What RFC 7489 Requires for Aggregate Reports

A DMARC aggregate report is a MIME multipart/report message (Content-Type: multipart/report; report-type=dmarc) containing two parts: a human-readable text part and a machine-readable XML part. The XML part must follow a strict schema. Every element must contain a with source_ip, count, and policy_evaluated sub-elements, plus an block with DKIM and SPF results. Missing any required field is a violation.

In practice, none of this is optional - yet a large share of receivers send reports missing something.

The Most Common Non-Compliance Patterns

Missing required fields in ``

RFC 7489 requires each to include source_ip, count, and policy_evaluated. Some receivers omit count when it equals 1, or drop policy_evaluated entirely when no policy was evaluated. A strict schema validator rejects the record outright. A lenient parser imports it with null fields, corrupting your counts silently.

Out-of-sequence XML elements

The DMARC schema specifies element order. Some receivers generate elements out of sequence - policy_evaluated appearing before source_ip, or auth_results containing an empty element instead of omitting it. RelaxNG validators reject out-of-sequence elements. Most XML parsers are more permissive, so these reports import without error but are technically non-compliant.

Wrong MIME type or Content-Type header

RFC 7489 requires the XML part to have Content-Type: application/dmarc+xml. Some receivers send reports as text/plain with XML content, or use application/xml instead of application/dmarc+xml. If your ingestion pipeline routes by MIME type, these reports arrive but never reach the right parser. They pile up in a mailbox unread.

Non-UTF-8 encoding in XML

The DMARC schema requires UTF-8 encoding. Some receivers embed Latin-1 (ISO-8859-1) or Windows-1252 characters. This is a silent failure: the report arrives, your parser reads it, and either throws a character encoding error or produces garbled output for any non-ASCII domain names or IP addresses. The report looks fine in the mailbox. The data is garbage.

Missing `` block

When a message has no DKIM signature and fails SPF, the block should still be present, containing at minimum empty and elements. Some receivers omit the entire block when results are unavailable. RFC 7489 requires it to be present, even if empty. A schema validator will reject the record.

Reports bundled into a single ZIP with no manifest

RFC 7489 doesn't prohibit bundling multiple reports in one archive, but many receivers do exactly that - five separate report ZIPs crammed into one file. If your pipeline expects one report per ZIP, it imports only the first one and silently discards the rest. You lose 80% of your data and never know it happened.

Duplicate `` entries for the same source IP

Some receivers generate duplicate entries for the same source IP and policy combination, effectively double-counting those messages. Your portal shows inflated volumes for certain IPs. Nobody can explain why. The reports aren't empty - they look valid. They're just wrong.

How to Detect Non-Compliance Programmatically

Validate DMARC reports against the RFC 7489 RelaxNG schema using a tool like jing or a Python library that supports RELAX NG validation. Run this as a pre-processing step before importing reports into your analysis pipeline.

At minimum, enforce valid MIME structure: multipart/report with exactly two parts (text and XML), and the XML part must have Content-Type: application/dmarc+xml.

Log every parse failure with the raw XML content. Non-compliant reports often fail silently rather than loudly - they import with missing fields and your counts quietly become unreliable. Explicit logging is the only way to know how much data you're actually losing.

How DMARCFlow Handles Non-Compliant Reports

DMARCFlow's parser is built for real-world DMARC reports, not just the idealized RFC-clean version. When a report has out-of-order elements, missing optional fields, or non-standard MIME types, DMARCFlow recovers the data rather than rejecting the entire record. For duplicate records, DMARCFlow de-duplicates by source IP and policy combination, keeping the most recent entry when duplicates are detected. For reports with wrong MIME types, DMARCFlow uses content-type sniffing to detect XML inside text/plain wrappers and processes it correctly.

DMARCFlow also validates every incoming report against the RFC 7489 schema and flags non-compliance on the report record. You can see at a glance how many reports are non-compliant, which receivers are generating them, and how much of your total data they represent.

This matters beyond cleanliness. If a receiver sends non-compliant reports, you may be undercounting authentication failures. Non-compliance is a visibility problem as much as a format problem.

Practical Takeaways

If you run DMARC reports through a standard XML parser and never see parse errors, check your logging. You're probably not looking hard enough. Non-compliant reports tend to fail silently - they import with missing fields and your counts quietly become unreliable.

Validate your ingestion pipeline against the RFC 7489 schema. Log non-compliance events. Track which receivers are generating malformed reports and use that data when you negotiate with vendors or evaluate new email security platforms. Receiver-side DMARC compliance is not as well enforced as the spec implies, and the burden of handling non-compliance falls on domain owners.

Before moving to p=reject, you want as complete a picture as possible. Non-compliant reports in your analysis pipeline mean you're making policy decisions on incomplete data. DMARCFlow's parser is designed to handle reports that trip up standard parsers - recovering data from malformed reports rather than discarding it, and flagging non-compliance so you know how much to trust each receiver's data.

FAQ

Can a receiver send non-compliant reports and still be considered a DMARC participant?

Technically no - RFC 7489 requires receivers to send aggregate reports in the specified format. In practice, the ecosystem tolerates a wide range of deviations because domain owners have little leverage over receiver behavior. You can file a report with the receiver, but don't expect a fast resolution.

Does non-compliance affect my ability to move to p=reject?

Indirectly, yes. If your reports are incomplete because receivers are sending non-compliant data, you're making policy decisions based on incomplete information. Before moving to p=reject, you want as complete a picture as possible. Identifying and accounting for non-compliant reports is a prerequisite for a confident policy upgrade.

Should I reject reports that fail RFC validation?

Flag rather than reject. A report with a minor RFC violation may still contain valid data. Rejecting it means you lose whatever visibility you had. Import it with a non-compliance flag so your pipeline accounts for data quality.