| 12 min read

RFC 9989, 9990, and 9991 are published: what changed for DMARC

DMARC is now an IETF Standards Track protocol. RFC 9989, 9990, and 9991 replace RFC 7489 from 2015. Records still start with v=DMARC1, so most production deployments need nothing immediate. But the spec has real changes: new tags, the Public Suffix List is out, the p=reject guidance has flipped, and aggregate reports gained fields. Here is what changed and what to do about it.

ML
Marc Lelu
RFC 9989, 9990, and 9991 are published: what changed for DMARC

The IETF working group spent more than seven years on what most of us were calling DMARCbis. In May 2026 it landed as three Standards Track RFCs:

This is the first substantive update to the DMARC specification since the original RFC 7489 in 2015. It is also the first time DMARC has formal IETF endorsement: RFC 7489 was published via the Independent Submission Stream as Informational, not by the IETF itself. RFC 9989 is Proposed Standard.

There is no “DMARC2”. Records still begin with v=DMARC1 and every deployment in the wild keeps working. The spec underneath has changed in ways that are worth reading carefully though.

Last updated: May 23, 2026.

Disclosure: DMARCTrust is our product. This is a working postmaster’s read of the new RFCs: what changed, what matters in production, and what you should actually do about it.

What actually changed

Pulled from Appendix C of RFC 9989:

  1. Standards Track, not Informational. DMARC is now a Proposed Standard with formal IETF endorsement.
  2. The single original document is now three: base protocol (9989), aggregate reports (9990), failure reports (9991). They can evolve independently without re-opening the others.
  3. The Public Suffix List is replaced by a DNS Tree Walk. Organizational Domain discovery no longer depends on the publicsuffix.org list.
  4. Three new tags: np (non-existent subdomain policy), psd (public suffix domain flag), t (test mode flag).
  5. Three tags removed: pct, rf, ri.
  6. The p=reject guidance is reversed. The new spec discourages p=reject for domains that host general user mailboxes, and tells receivers to treat p=reject as p=quarantine by default.
  7. There is now a conformance section. RFC 9989 §8 lists explicit MUSTs for full participation by domain owners and mail receivers.
  8. The aggregate report schema gained discovery_method, np, and testing elements in policy_published. DKIM selector is now required when reporting a DKIM signature.

v=DMARC1 is preserved. No existing record needs to change today.

p=reject is no longer the recommended end state

For a decade, the standard advice has been: monitor at p=none, move to p=quarantine, finish at p=reject. RFC 9989 pushes back on the last step. The language is direct.

From §7.4:

It is therefore critical that Mail Receivers MUST NOT reject incoming messages solely on the basis of a “p=reject” policy by the sending domain. Mail Receivers must use the DMARC policy as part of their disposition decision, along with other knowledge and analysis. […] In the absence of other knowledge and analysis, Mail Receivers MUST treat such failing mail as if the policy were “p=quarantine” rather than “p=reject”.

And from Appendix C.6:

In particular, this document makes explicit that domains for general-purpose email SHOULD NOT deploy a DMARC policy of “p=reject”.

The reasoning is the indirect-mail-flow problem that has dogged DMARC since day one. When a user posts to a mailing list, the list resends the message with the original From: header intact. SPF breaks (the list’s IP is not in the user’s SPF record), and DKIM often breaks too (the list rewrites the subject, adds a footer, or otherwise modifies the body). Under p=reject, the list’s subscribers stop receiving the message, the list software interprets the bounces as a dead address, and it auto-unsubscribes them.

The working group spent a decade waiting for ARC (RFC 8617) or some other technical fix to gain widespread adoption. None has. RFC 9989 just accepts that fact and adjusts the guidance.

In practice:

  • If your domain is a transactional sender that never has humans posting from it (bank notifications, e-commerce receipts, dedicated marketing subdomains), p=reject may still be appropriate.
  • If your domain hosts employee or customer mailboxes, where a human might subscribe to an external mailing list, p=quarantine is the spec’s recommended end state. Not a stop on the way to reject.
  • For subdomains that never send mail, use sp=reject on the parent. That part of the playbook does not change.

This is the change most worth a second look at your current setup. We updated our p=none to enforcement migration playbook to reflect the new guidance.

The new tags: np, psd, t

np: policy for non-existent subdomains

np sets the assessment policy for subdomains that do not exist in DNS. It mirrors p and sp syntactically:

v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:[email protected]

The case for it: an attacker spoofs support.yourcompany.com even though you have never published that subdomain. np=reject makes spoofing those names visibly harder for the attacker without affecting any subdomain you do operate. If you do not publish np, the policy from sp (or p as fallback) applies.

np was first introduced in RFC 9091 as part of an experimental PSD profile. RFC 9989 imports it and obsoletes RFC 9091.

psd: Public Suffix Domain flag

psd is a flag, not a policy. It tells DMARC processors whether a domain is itself a public suffix (such as .gov.uk or .bank). The valid values are y (yes, this is a PSD), n (no, this is the Organizational Domain), and u (unknown), which is the default and means “look it up via Tree Walk”. Almost no end-user domain owner needs to set this. It is published by Public Suffix Operators: country-code registries, gTLD operators, or organizations like fTLD that manage .bank. The psd tag is what allows DMARC to apply at a TLD-like level without needing an offline registry.

t: test mode flag

t replaces the most useful part of pct. It is a binary flag, not a percentage:

  • t=n (default): apply the declared policy as-is.
  • t=y: request that receivers apply the next-lower policy instead. If p=reject; t=y, receivers treat the message as if p=quarantine. If p=quarantine; t=y, receivers treat it as p=none.

t=y does not affect aggregate or failure report generation, so you keep visibility while you test. It is much coarser than pct=25 → pct=50 → pct=100, but RFC 9989 Appendix A.6 explains the trade-off: the working group found that the pct tag was usually not applied accurately unless the value was 0 or 100. Implementations differed widely on what they did with intermediate values, so the gradual percentage rollout was less deterministic than it looked.

A reasonable use of t=y is a one- or two-week dry run before flipping to a stricter policy.

The removed tags: pct, rf, ri

  • pct, the percentage tag. Removed. Use t=y for staged rollouts instead.
  • rf, failure report format. Removed. Only one format was ever used.
  • ri, requested aggregate report interval. Removed. RFC 9989 §8 instead recommends receivers send reports daily.

If you publish records with any of these tags today, nothing breaks. Receivers continue to parse the records and silently ignore the unknown tags (RFC 9989 §4.7 still says “unknown tags MUST be ignored”). For new records or playbooks, leave them out.

Public Suffix List out, DNS Tree Walk in

What changed

RFC 7489 described an offline lookup against a “Public Suffix List”, typically the one maintained at publicsuffix.org, to determine where a domain’s “organizational” boundary was. The Organizational Domain is what DMARC uses to find the policy record when no record exists at the Author Domain itself, and to determine relaxed alignment between two domains.

The PSL approach had a few operational issues. Different receivers used different PSL snapshots, so they could disagree about the Organizational Domain of the same address. There was no formal update cadence. And it depended on an out-of-band registry that the protocol itself never specified.

RFC 9989 §4.10 replaces this with a “DNS Tree Walk”: the receiver queries _dmarc.<author-domain>, then _dmarc.<parent>, and so on up the tree. The walk is capped at eight queries (the longest names in observed data have seven labels). When a record carries psd=n, the walker stops and treats that name as the Organizational Domain. When a record carries psd=y, the Organizational Domain is one level below. Otherwise, the policy record at the shortest qualifying name wins.

Why it matters

For a flat domain like example.com → mail.example.com, the Tree Walk produces exactly the same Organizational Domain that the PSL did. No behavior change.

For multi-tier domains, the Tree Walk now supports legitimate per-subtree policies. An organization can publish p=quarantine at _dmarc.email.example.com and p=reject at _dmarc.example.com, and both apply at their respective levels. Under the PSL, the parent record would have shadowed everything below.

In practice, the more common effect will be the inverse. Receivers that previously fell back to a single parent policy will now see whatever subdomain records exist, including misconfigured ones nobody knew were live. If you start seeing failures in your aggregate reports attributed to _dmarc.bounce.something.example.com, that is the Tree Walk surfacing a record you forgot about.

Reports now tell you which method was used

RFC 9990 §3.1.1.5 adds a discovery_method element to policy_published with the value psl or treewalk. This lets you see, per report, whether the sending receiver has migrated to the new algorithm. Expect a long mixed period. Large receivers will move at different paces.

Aggregate report format changes (RFC 9990)

The XML schema is the same shape, with a handful of additions and one tightening. Drawn from Appendix C of RFC 9990:

  • A new discovery_method element in policy_published, with the value psl or treewalk (above).
  • A new np element in policy_published, carrying the non-existent subdomain policy.
  • A new testing element in policy_published, carrying the value of the t tag.
  • DKIM selector is now required when a DKIM signature is reported. Under RFC 7489 it was implicitly optional, and many receivers left it out. That made it hard to correlate failures with key rotations.
  • Namespaced extensions are formally allowed in the report root, the policy element, and individual records.
  • The report identifier has more structure to reduce duplicate-detection ambiguity.

The new elements are additive. The DKIM selector requirement is the only tightening. Existing parsers that ignore unknown elements keep working, but fixtures and validators should include selectors when they report DKIM signatures under RFC 9990.

Failure report changes (RFC 9991)

Minor. The main additions formalize the ARF (Abuse Reporting Format) header fields a DMARC failure report must include:

  • Identity-Alignment is now REQUIRED and carries the comma-separated list of mechanisms (dkim, spf) that failed to authenticate an aligned identifier.
  • DKIM-Domain, DKIM-Identity, DKIM-Selector are REQUIRED when reporting a DKIM failure of an aligned identifier.
  • SPF-DNS is REQUIRED for SPF failure of an aligned identifier.
  • A dmarc Authentication Failure Type is defined for the Auth-Failure field, used when failure reports cover DMARC alignment failures rather than the underlying SPF or DKIM checks.

RFC 9991 §7 also adds a privacy section, acknowledging that failure reports can leak PII from message bodies and headers, and recommending that report generators redact local-parts where possible. Many large providers already cap or disable failure reporting for this reason.

The conformance section

RFC 9989 §8 lists explicit MUSTs for “Full DMARC Participation”. This is the first time the spec has stated, in normative language, what counts as compliance.

A Domain Owner that fully participates:

  • MUST send mail that produces an SPF-Authenticated Identifier aligned with the Author Domain.
  • MUST send mail with a DKIM Signing Domain that produces a DKIM-Authenticated Identifier aligned with the Author Domain.
  • MUST set up a mailbox to receive aggregate reports, and collect and analyze them.
  • MUST publish a DMARC Policy Record for the Author Domain, and for the Organizational Domain if it differs.
  • MUST NOT rely solely on SPF for a DMARC pass if the policy is p=reject.

A Mail Receiver that fully participates:

  • MUST check for a DMARC Policy Record on inbound mail.
  • MUST determine if Authenticated Identifiers exist and preserve the results for reporting.
  • MUST conduct Identifier Alignment checks when applicable.
  • MUST support the mailto: URI for reports.
  • SHOULD send aggregate reports at least daily.
  • MUST NOT reject messages solely on the basis of p=reject.

The list is short enough to use as an audit checklist. Most established DMARC monitoring deployments already satisfy the domain-owner side.

What you should actually do

A short, honest list:

  1. Revisit your current p=reject decision. If you host user mailboxes, the spec now recommends p=quarantine as the end state. This is the one change worth taking time over.
  2. Stop using pct= in new records or playbooks. If you currently publish p=reject; pct=100, drop the pct. If you are mid-rollout with pct=25, that policy is non-deterministic in practice; use t=y instead for the test-mode period.
  3. Consider np=reject if your domain has no subdomain senders.
  4. Watch for discovery_method=treewalk appearing in your aggregate reports. It tells you which receivers have migrated.
  5. Audit subdomain DMARC records if you have a complex DNS tree. The Tree Walk will start applying them where the PSL previously fell back to the parent.
  6. Nothing else is urgent. v=DMARC1 keeps working. The transition is gradual and backward-compatible.

DMARCTrust ingests aggregate reports exactly as before. Our DMARC checker and dashboard already surface the data you need to make these decisions. We have updated the DMARC tags guide, the alignment guide, and the migration playbook to match the new spec.

For the authoritative text, read the RFCs directly. They are clearer than most IETF specifications:

Read Next

View all posts
From p=none to p=reject: how to enable DMARC enforcement in 2026
dmarc-setup ·

From p=none to p=reject: how to enable DMARC enforcement in 2026

Forums show the same anxiety pattern: 'I want p=reject, but I'm afraid I'll block legit mail.' The rollout is mostly about gates: inventory done, alignment fixed, SPF lookup limit avoided, and then staged enforcement.

DT
DMARCTrust
3 min read
Who is sending mail as us? The Shadow IT sender inventory problem
dmarc-monitoring ·

Who is sending mail as us? The Shadow IT sender inventory problem

The biggest practical blocker to moving beyond p=none isn't DNS syntax. It's discovering every legitimate sender. DMARC reports expose these "unknown senders" and Shadow IT that you didn't even know existed.

DT
DMARCTrust
4 min read

Need expert help with email deliverability?

Hire an email deliverability consultant who has shipped billions of emails. Free assessment, hands-on engagement, written quote before any work starts.