~/blog

Your DMARC record still has pct=100. It is not a tag anymore.

published

#dns#email#security

TL;DR

DMARC became a real IETF standard in May 2026: RFC 9989 (core), RFC 9990 (aggregate reporting) and RFC 9991 (failure reporting) obsolete RFC 7489. Your v=, p=, sp=, rua=, ruf=, adkim=, aspf= and fo= tags all still mean what they meant. Three tags do not survive: pct, rf and ri. And the Organizational Domain — the thing that decides whether relaxed alignment passes — is no longer looked up in the Public Suffix List.

The problem

Nearly every DMARC record in the wild was copied from a 2015-era blog post, and nearly every one of those posts ends with the same line:

v=DMARC1; p=quarantine; pct=100; rua=mailto:[email protected]; rf=afrf; ri=86400

Three of those six tags no longer exist in the specification. pct was removed outright — RFC 9989 discusses the removal in Appendix A.6 and points at a t (test mode) tag instead, which is a boolean rather than a percentage. rf and ri are simply absent from the list of valid tags in Section 4.7 of the new core document.

Nothing breaks the day you read this. RFC 9989 requires receivers to ignore unknown tags, which is exactly why the dead ones have survived a decade of copy-paste. But pct=50 in particular was never doing what most people believed it was doing, and now it is not even a knob — a record that relies on it for a staged rollout is relying on nothing.

Why it happens

RFC 7489 was never an IETF standard. It was published in 2015 through the Independent Submission Stream with category Informational — a description of what a group of large mailbox providers had already deployed, not something the IETF had reviewed and blessed. Everything built on top of it inherited that looseness, including a decade of implementation drift the spec never adjudicated.

DMARCbis is the correction. The three new documents are Standards Track, they split the protocol from its two reporting formats, and they resolve the ambiguities that vendors had each been resolving privately. RFC 9989 also obsoletes RFC 9091, the experimental Public Suffix Domain extension, folding that work into the core.

The change with actual behavioural teeth is the Organizational Domain lookup. Under RFC 7489, a receiver checking whether mail.corp.example.co.uk aligns with example.co.uk consulted the Public Suffix List — a file maintained outside the DNS, shipped inside browsers and libraries, and updated on somebody else’s release cycle. RFC 9989 replaces that with what it calls the DNS Tree Walk: the receiver queries up the name, label by label, looking for a DMARC policy record, capped at eight queries per message as an anti-abuse measure. The boundary is now discovered in the DNS by the domain owner publishing records, instead of asserted by a static list.

For a normal example.com with one DMARC record at _dmarc.example.com, the result is identical. For deep subdomain trees, multi-level public suffixes, and anyone who was fighting the PSL to get a subdomain treated as its own organization, the result can differ — and now there is a supported way to say what you mean.

What to do

1. Strip the dead tags at your next DNS edit

TagRFC 7489RFC 9989
v, p, sppolicyunchanged
adkim, aspfalignment mode (r / s)unchanged
rua, ruf, foreportingunchanged (formats now in RFC 9990 / 9991)
pctsample percentageremoved — see t (test mode)
rffailure report formatremoved
riaggregate report intervalremoved

Before:

_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; pct=100; rua=mailto:[email protected]; rf=afrf; ri=86400"

After:

_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:[email protected]; fo=1"

This is not urgent-urgent. Unknown tags are ignored, so the old record keeps working. Treat it as cleanup on the next edit you were making anyway — and stop pasting the dead tags into new records.

2. Stop using pct as your rollout plan

If your rollout was p=reject; pct=10, you no longer have a rollout plan, you have p=reject with a tag that some receivers honour and the current standard does not define. The supported staging path is the one that always actually worked: p=none while you read aggregate reports, then p=quarantine, then p=reject, moving only when the reports show every legitimate sender aligned.

3. Re-check alignment, because that is still what fails

None of this changes the rule that catches people out. DMARC passes only if SPF or DKIM passes and the passing identifier is aligned with the From: header domain. SPF authenticates the envelope sender, so a message sent through an ESP with Return-Path: [email protected] produces a perfectly valid SPF pass that contributes nothing to DMARC. If the DKIM signature’s d= is also the ESP’s domain, DMARC fails on a message where both underlying checks passed.

The fix is unchanged: a custom return-path on your own domain, DKIM signing with d= set to your domain, or both. To see what your domain currently publishes — the DMARC record itself, plus SPF and DKIM — the email auth checker does the DNS lookups and reads the tags back to you, which is also the fastest way to spot a pct= you forgot about.

Caveats

References