What actually changed in DMARC in May 2026?
DMARC stopped being a convention. RFC 9989 published in May 2026 as a Standards Track document, obsoleting both RFC 7489 — the 2015 specification, which was only Informational — and RFC 9091, which had defined Public Suffix Domain handling separately. Reporting moved out into two companion specifications published the same month: RFC 9990 for aggregate reports and RFC 9991 for failure reports. That change of status matters more than it sounds. For eleven years, the document everyone built enforcement on was formally an informational write-up of what a few large operators had agreed to do among themselves, and compliance was a matter of convention. RFC 9989 makes it a standard with a tag registry, and the specification is explicit that tags outside that registry are not part of DMARC.
Four changes carry operational consequences:
| Change | What it means | Who it affects |
|---|---|---|
pct= retired | Percentage-based partial enforcement is marked historic and no longer part of the spec | Anyone still running pct below 100 |
t= added | Test mode: receivers apply one policy level below what you publish | Anyone staging a policy rollout |
np= added | Separate policy for subdomains that do not exist | Domains whose sp= is weaker than p= |
| DNS Tree Walk | Organizational domain found by walking the DNS tree, not a Public Suffix List alone | Multi-level domains, PSD operators |
RFC 9989 also removes rf= and ri=. Both governed report formatting and interval, and both are now the business of the reporting RFCs rather than the policy record.
Is my existing DMARC record now invalid?
No — and that is precisely why this goes unnoticed. RFC 9989 states that a receiver must ignore tags it does not recognise: "Unknown tags MUST be ignored." A leftover pct=100 produces no error, no bounce, and no report. It is simply not read. So nothing breaks, and no monitoring fires. Your DMARC checker very likely still shows a green tick, because it is verifying that the record parses, not that the record still means what you intended when you wrote it. This is the same failure mode as every other authentication regression: nobody emails you an error when your SPF breaks, and nobody is going to email you when a tag in your DMARC record quietly stops being part of the specification either. The gap between "parses" and "does what you meant" is where the risk lives. A conformant record and a correct record are different claims, and at most companies only the first one is being checked.
What happens to my pct= tag?
Your policy gets stricter than you set it, on someone else's schedule. Under RFC 7489, p=quarantine; pct=10 meant: apply quarantine to roughly 10% of failing mail and leave the rest alone. It was the standard way to roll out enforcement without breaking a stream you had not fully audited. Because RFC 9989 retires the tag to historic, a receiver implementing the new specification ignores it and applies p= to everything that fails.
The working group's stated reason for cutting it is worth reading: RFC 9989 records that pct was ambiguous from the start — receivers never agreed whether pct=10 meant 10% of all mail or 10% of failing mail — and in practice it was honoured accurately only at the extremes, 0 or 100. The deeper problem, as Douglas Foster put it in the working-group thread that drove the decision, is that the percentage is measured against traffic you do not control: "The actual volume of impersonating messages will be determined by the spammer, not by the domain owner, so the whole notion of choosing a percentage is flawed."
For most records this is a no-op, since RFC 7489 already specified pct=100 as the default. For anyone mid-rollout it is not: under RFC 9989 a record set to quarantine 10% of failures now quarantines all of them, a tenfold change in enforcement triggered by nothing the domain owner did, arriving whenever each receiver ships its update. Receivers move on their own timelines, so this does not land everywhere on one day — it lands unevenly, which is considerably harder to diagnose than a clean cutover.
The replacement is t=, defined in RFC 9989 as policy test mode. Setting t=y tells receivers you are testing, and they apply a policy one level below what you published:
p=reject; t=ybehaves as quarantine.p=quarantine; t=ybehaves as none.t=n, the default, means the published policy applies in full.
It expresses the same intent as a staged rollout without sampling — every message gets the softened policy, rather than a random slice getting the full one, which also makes the results far easier to read in an aggregate report.
Can I just replace pct= with t=y?
Not as a like-for-like swap — and getting this wrong points toward more enforcement, not less. The softening only happens on receivers that have implemented RFC 9989, which is the part most coverage of the new tag leaves out. Both specifications tell receivers to ignore tags they do not recognise, so a receiver still running RFC 7489 reads p=reject; t=y, discards the t= it has never heard of, and applies p=reject in full. During the transition your record therefore means two different things at once:
| Record | RFC 9989 receiver | RFC 7489 receiver |
|---|---|---|
p=reject; t=y | Quarantine (softened) | Reject — in full |
p=quarantine; t=y | None (softened) | Quarantine — in full |
p=quarantine; pct=10 | Quarantine, all failing mail | Quarantine, ~10% of failing mail |
Read the first row against the third and the trap is obvious. A domain sitting at p=reject; pct=10 that "modernises" to p=reject; t=y has just gone from roughly a tenth of failing mail being rejected to all of it being rejected, everywhere the new specification has not landed yet — the exact escalation the tag was supposed to help you avoid, arriving from the opposite direction. If you need a specific disposition to hold consistently across every receiver, publish it in p=, because p= is the only field both specifications read the same way. Want monitoring while you audit? Publish p=none. Want quarantine to mean quarantine everywhere? Publish p=quarantine. Treat t=y as a signal to new receivers layered on top of a policy that must already be safe on its own — not as a dial that softens what you published.
Under RFC 9989, if your live record still carries pct= below 100, that is the one item on this page to fix today — but fix it by deciding what p= should actually be, not by moving the ambiguity into a tag half the internet has not implemented. You can check what your record resolves to right now in a few seconds.
Do I need the new np= tag?
Probably not — and the widespread claim that every domain needs it is wrong. This is worth spelling out, because a good deal of coverage describes the tag as a hole in any record lacking it. RFC 9989 defines the fallback explicitly: "If the 'np' tag is absent, the policy specified by the 'sp' tag (if the 'sp' tag is present) or the policy specified by the 'p' tag (if the 'sp' tag is not present) MUST be applied for non-existent subdomains." The chain is np → sp → p. A domain publishing p=reject with no sp= and no np= therefore already rejects mail from subdomains that do not exist, and adding np=reject to that record changes nothing at all.
np= earns its place whenever you want non-existent subdomains handled differently from whatever the np → sp → p fallback would already give them — which in practice almost always means one configuration: when your subdomain policy is weaker than your apex policy. A record reading p=reject; sp=none locks the apex and then hands every subdomain — including the infinite set that has never existed — a policy of none. That is the shape an attacker wants, and it is common, because sp=none is what teams reach for when a marketing subdomain or a vendor-run sending host starts failing alignment and nobody has time to fix it properly. In that case np=reject is the fix, and it is close to free, because no legitimate mail originates from a hostname that does not resolve. You keep sp=none for the real subdomain you have not cleaned up yet, and you close the door on the ones that do not exist.
What did we find when we checked the biggest companies in America?
Method: we took the Fortune 50 — the fifty largest US companies by revenue on the 2026 Fortune 500 — resolved each company's primary corporate domain, and queried its _dmarc TXT record over public DNS in July 2026. All fifty publish a DMARC record. This is deliberately the opposite of a weak sample: if any group has the security staff, the budget, and the outside scrutiny to keep a DMARC record current, it is the most valuable companies in the country.
| Measure | Result |
|---|---|
| Publish a DMARC record | 50 of 50 |
Enforce (p=quarantine or p=reject) | 46 of 50 |
Carry at least one tag RFC 9989 removed (pct, rf, ri) | 17 of 50 — 34% |
Publish the new np= tag | 0 of 50 |
Use the new t= staging tag | 0 of 50 |
sp= weaker than the apex, with no np= | 2 of 50 |
Still at p=none — monitoring only, no enforcement | 4 of 50 |
Three things stand out. Adoption of the new tags is zero — not low, zero. Two months after a Standards Track publication, not one of the fifty most valuable companies in America publishes np= or t=. These are organizations with dedicated messaging-security teams and eight-figure security budgets. Whatever channel is supposed to carry "the specification changed" to the people who maintain these records did not reach even them.
A third still carry tags the spec retired. Seventeen of the fifty publish pct, rf, or ri, tags RFC 9989 moved to historic in the IANA registry and that conformant receivers no longer read. In this sample every pct value is pct=100, so the tenfold-escalation trap described above is not armed here — the tags are inert. But inert-and-wrong is a precise proxy for the same thing it signalled in every other sample: a record last edited against a specification that no longer exists.
Enforcement itself is not universal — and it is worth being careful about calling that a failure. Four of the fifty still sit at p=none, which asks receivers to change nothing about delivery, though it does switch on the aggregate reporting stream: a monitoring posture rather than an empty one. RFC 9989 is explicit that enforcement is not a universal good. Section 7.4 states that domains hosting users who might post to mailing lists "SHOULD NOT publish Domain Owner Assessment Policies of 'p=reject'", and — pointing the other way — that Mail Receivers "MUST NOT reject incoming messages solely on the basis of a 'p=reject' policy by the sending domain." So "46 of 50 enforce" describes posture, not merit: a general-purpose domain deliberately sitting at p=quarantine may be better configured than a peer at p=reject.
The sharper finding is internal contradiction rather than leniency. Two pair an apex policy of reject with a subdomain policy weaker than reject and no np= to cover the non-existent case: the p=reject; sp=none shape that bolts the front door and props open every side one, at companies whose names you would recognise. That shape is hard to read as intentional — it claims strict handling at the apex while leaving every never-registered subdomain wide open, and exact-domain impersonation is the one thing DMARC at enforcement genuinely shuts down. For scale on the surrounding category, the FBI's Internet Crime Complaint Center recorded $3.05 billion in reported US business email compromise losses in 2025 across 24,768 complaints. That figure is not a measure of what DMARC would have prevented: IC3's BEC category is dominated by compromised accounts and lookalike domains, and mail sent from a genuinely compromised mailbox authenticates correctly, so no DMARC policy touches it. Exact-domain spoofing is the slice DMARC closes, and the honest claim is that it closes that slice — not that it recovers the total. If your own posture looks shakier than you expected once you actually read the record, the question underneath is often how do I know if my email list is hurting deliverability — authentication posture and list posture fail independently and get diagnosed together.