Learn

RFC 9989: what changed in DMARC

RFC 9989, published in May 2026, replaces RFC 7489 as the DMARC standard: it removes the pct, rf and ri tags, adds t, np and psd, and finds the Organizational Domain with a DNS Tree Walk instead of a Public Suffix List. A record that uses only v, p, rua, adkim and aspf stays valid without any edit.

By Ahmet Karaca · Updated · Checked against RFC 9989 (DMARC, May 2026), RFC 9990 (DMARC aggregate reports), RFC 9991 (DMARC failure reports) and RFC 7489 (DMARC) on

What changed at a glance

DMARC was first published in 2015 as RFC 7489, an informational document outside any IETF work stream. The IETF's revision, developed as the draft draft-ietf-dmarc-dmarcbis and often called DMARCbis, came out in May 2026 as three documents: RFC 9989 defines the protocol, RFC 9990 defines aggregate reports and RFC 9991 defines failure reports. Together they obsolete RFC 7489. RFC 9989 also obsoletes RFC 9091, the experiment that introduced DMARC for public suffix domains.

The version tag did not change: records still start with v=DMARC1. Appendix C of RFC 9989 lists the differences, and these are the ones that touch a published record:

Differences between RFC 7489 and RFC 9989
TopicRFC 7489RFC 9989
StatusInformational, published by the Independent Submissions EditorInternet Standards Track, from the IETF
Organizational DomainThe registered domain, found with a Public Suffix ListFound with a DNS Tree Walk over DMARC records
pct0 to 100, default 100Removed; listed as historic
tDid not existNew: y or n, default n
npNot in RFC 7489 (experimental in RFC 9091)Added to the standard
psdDid not existNew: y, n or u, default u
rfFailure report format, default afrfRemoved; listed as historic
riReport interval in seconds, default 86400Removed; listed as historic
Size limit on report URIs (!10m)AllowedRemoved; reporters should ignore it
pRequired, after vRecommended; see the rules for a missing p
Number of report addressesReceivers could limit it, with a minimum of twoA report should go to each listed address
Reporting specificationInside RFC 7489RFC 9990 (aggregate), RFC 9991 (failure)

If DMARC itself is new to you, start with What is DMARC? The DMARC record tags reference has each current tag with its values and default.

The pct tag is gone

pct asked receivers to apply your policy to a percentage of failing mail, so that a domain could step up gradually. Appendix A.6 of RFC 9989 says operational experience showed the tag "was usually not accurately applied, unless the value specified was either 0 or 100", and that the inaccuracies varied widely between implementations. So pct=25 never reliably meant a quarter of your failing mail.

pct=0 had taken on a different job. Some intermediaries and mailbox providers read it as a signal to deviate from standard handling, usually by rewriting the From header to avoid DMARC failures downstream. The authors found that useful enough to keep, but saw no sense in a percentage tag with two meaningful values. That function moved to the new t tag.

RFC 9989 no longer lists pct among the valid tags in section 4.7 or gives it a value rule in section 4.8, and the IANA table in section 9.3 marks it historic. It defines no behaviour for a pct left in a record. A receiver still built on RFC 7489 follows the old rule in section 6.6.4 of that document, so the same pct=25 can mean different things to different receivers. That alone is a reason to take it out.

What t=y means, in the RFC's words

Descriptions of t=y vary, so it is worth reading the definition in section 4.7 of RFC 9989 directly. It calls the value "a request that the actor performing the DMARC validation check not apply the policy, but instead apply any special handling rules it might have in place, such as rewriting the RFC5322.From header field". It then states what the domain owner expects: "the policy applied to any failing messages will be one level below the specified policy".

The same section works through both cases, which gives this table:

How the t tag changes the expected handling of failing mail
PublishedExpected handling
p=none; t=yNo change: t has no effect on a policy of none
p=quarantine; t=yFailing mail gets a policy of none
p=reject; t=yFailing mail gets a policy of quarantine
p=reject (t absent or t=n)The policy applies as published

Four details follow from the text:

  • p=reject; t=y is not monitoring. The published reject is held back, but failing mail is still expected to be quarantined.
  • It covers every policy tag. The definition names the policy declared in p, sp and np.
  • Reports keep coming. The tag "does not affect the generation of DMARC reports". RFC 9990 adds a testing field to policy_published and a policy_test_mode override reason, so a report can show that testing mode was in play.
  • It is a request. Section 5.4 leaves the final handling of any message to the receiver, and that includes mail under a testing policy.

Appendix A.6 describes t=y and t=n as meant to be analogous to pct=0 and pct=100. The old rule lines up: section 6.6.4 of RFC 7489 says mail outside the percentage under p=reject is treated as quarantine, and under p=quarantine gets normal local classification.

rf, ri and the report size limit are gone too

rf named the format for failure reports, and RFC 7489 allowed only one value, afrf. ri asked for a reporting interval in seconds, with a default of 86400 and anything other than a daily report understood as best effort. Neither tag appears in RFC 9989, and section 9.3 marks both historic. Receivers still have a target for frequency: section 8 says they should send aggregate reports at least daily, and RFC 9990 describes the reports as daily or more frequent.

The optional size limit on a report address, such as mailto:dmarc@example.com!10m, has also been removed. The grammar in section 4.8 keeps it only as obsolete syntax that reporters should ignore. None of this affects delivery, so cleaning these up is tidying rather than an emergency.

The DNS Tree Walk replaces the Public Suffix List

Relaxed alignment and policy inheritance both depend on knowing a name's Organizational Domain. RFC 7489 defined it as the domain registered with a registrar and pointed receivers at a Public Suffix List. It required no specific list and gave no guidance on how often to refresh one, and it acknowledged that receivers using different lists could reach different answers. RFC 9989 replaces that reliance with a process that reads the answer from DNS.

The walk asks for a DMARC record at _dmarc. plus the starting name, then removes the left-most label and asks again, up to the top of the name. It discards answers that do not start with v=DMARC1, discards all the records at a name that has more than one, and stops as soon as it finds a single record with psd=y or psd=n. A name with eight or more labels is cut down to seven after the first query, so a sender cannot force an unlimited number of lookups. This is the RFC's own example of the eight queries for a very long name:

_dmarc.a.b.c.d.e.f.g.h.i.j.mail.example.com
_dmarc.g.h.i.j.mail.example.com
_dmarc.h.i.j.mail.example.com
_dmarc.i.j.mail.example.com
_dmarc.j.mail.example.com
_dmarc.mail.example.com
_dmarc.example.com
_dmarc.com

From the records it found, working from the longest name to the shortest, the receiver picks the Organizational Domain: a record with psd=n is it; a record with psd=y, other than at the starting name, makes the name one label below it the answer; otherwise the record with the fewest labels wins. If no record turns up, the starting name is its own Organizational Domain. DMARC alignment explained goes through this with worked examples.

Policy discovery uses the same walk with one difference. The receiver first looks at the From domain itself. If a valid record is there, that record applies and no further lookup is needed to find the policy. Only if not does the walk continue, from the parent, to the Organizational Domain's record and then a public suffix domain's.

Where the old and new methods disagree

When your registered domain publishes the record that covers its subdomains, both methods name the same Organizational Domain. They can part ways when the record sits elsewhere. Suppose only mail.example.com publishes a record, and example.com and com publish none. The walk makes mail.example.com the Organizational Domain of news.mail.example.com, while example.com is its own. A signature with d=example.com on mail from news.mail.example.com does not align under the walk, yet a list-based receiver would call both names example.com and align them.

Appendix C.3 of RFC 9989 acknowledges this kind of gap between Tree Walk receivers and receivers still built on RFC 7489. It says the issue is entirely avoided by using strict alignment and publishing explicit DMARC records for all the From domains your organization uses. Explicit records are the cheap half of that advice.

The np and psd tags

np sets the policy for subdomains that do not exist, imported from the RFC 9091 experiment. Non-existent has a precise meaning: a DNS query for the name returns NXDOMAIN. Appendix A.4 stresses that DMARC does not use the narrower mail test of having an MX, A or AAAA record, so a name with any record at all exists. When np is absent, sp applies, and when that is absent too, p does. If you want invented names under your domain handled more strictly than real ones, this is the tag to use.

psd says whether a domain is a public suffix domain, and it is what lets the walk work without a list. It takes three values:

  • y: published by public suffix operators to mark the domain as a public suffix.
  • n: not a public suffix, and the Organizational Domain for itself and its subdomains.
  • u: the default, which leaves the answer to the Tree Walk.

A domain owner has two reasons to reach for psd=n. Section 5.1.8 describes large organizations that delegate parts of their DNS to departments, where it draws a boundary between them. Section 11.8 suggests it if a public suffix above you publishes a record without psd=y, so that record is not mistaken for your Organizational Domain. The note at the end of section 4.10.1 limits what a suffix operator can do: its policy is not used for an Organizational Domain that has published its own record.

New language around p=reject

One change involves no tag. Appendix C.6 says RFC 9989 now makes explicit that general-purpose email domains should not deploy p=reject, and section 7.4 gives the rules:

  • Domains that publish p=reject must not rely solely on SPF for a DMARC pass and must apply valid DKIM signatures, because relaying usually breaks SPF while DKIM generally survives.
  • Domains that host users who might post to mailing lists should not publish p=reject. Those that want to anyway should first publish p=none for at least a month and p=quarantine for as long again, compare the results, and tell their users that list participation may suffer.
  • Receivers must not reject mail solely because of p=reject. Without other knowledge and analysis, they must treat it as if the policy were quarantine.

Do not lean on that last rule. The same section admits that few receivers apply such mitigations and that forwarded list mail with an unmodified From line is frequently rejected. Moving from p=none to p=reject covers how to weigh this for your domain.

What a domain owner must actually do

Nothing breaks on the day a new RFC appears, and RFC 9989 does not ask you to republish a valid record. The real work is a short checklist:

  1. Remove what no longer exists. Take out rf, ri, pct values from 1 to 100 and any !size suffix on report addresses.
  2. Decide what a partial pct was for. If you were partway through a rollout, either publish the full policy or go to testing mode. Do not leave pct=25 in place expecting a quarter.
  3. Publish a record for each From domain and for its Organizational Domain. Section 8 lists this as required for full participation, and explicit records remove most of the ambiguity described above.
  4. Align both SPF and DKIM. A message needs only one aligned pass, but section 8 asks for both for full participation, and DKIM becomes mandatory if you publish p=reject.
  5. Keep reading aggregate reports. Section 5.1.5 calls them the way to audit your own mail streams before you enforce. If reports go to another organization's domain, that domain must still publish the authorization record described in section 4 of RFC 9990.
  6. Leave psd out unless one of the situations above applies to you.

In most cases the cleanup looks like this. Before:

v=DMARC1; p=quarantine; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc@example.com!10m

After, with the same effect under RFC 9989:

v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com

To test a policy, RFC 9989 needs only t=y. Patchkit writes t=y; pct=0 together, which is our own compatibility choice and not something the RFC asks for. A receiver on RFC 7489 ignores t as an unknown tag and honours pct=0; a receiver on RFC 9989 acts on t=y. Because Appendix A.6 treats the two as analogous, both land on the same handling. Remove both tags when you finish testing.

v=DMARC1; p=reject; t=y; pct=0; rua=mailto:dmarc@example.com

How Patchkit follows RFC 9989

The DMARC checker finds the record the way section 4.10 describes: it starts at the domain you enter, walks up under the eight-query limit, and says when the record it found is inherited from another name. It also runs the check from RFC 9990 on the first three external report destinations. On the record itself, it notes rf and ri as removed, explains what a pct from 1 to 99 now means, reports invalid p, sp, np, adkim, aspf and t values, and notes when t=y has no pct=0 beside it. It does not validate the values of fo or psd; an undefined psd value counts as u, as the default does.

The DMARC generator writes t=y; pct=0 in testing mode, never writes a pct from 1 to 99, and drops rf, ri and partial pct values from a record you import, telling you when it does. It does not write np or psd, so add those by hand if you need them. In an account, the "Path to p=reject" card moves through p=none, quarantine (testing), quarantine, reject (testing) and reject; you publish each record yourself, since Patchkit does not write to your DNS.

One limit to know: the Tree Walk applies to checking a live domain. The DMARC report analyzer and the email header analyzer work out relaxed alignment from a Public Suffix List, without DNS queries. That gives the same answer when your registered domain publishes the record, and can differ in the setups described above. To turn reports into a running list of senders while you make these changes, see DMARC monitoring.

DMARC setup guides by service

Mailbox providers

Marketing platforms

Transactional email

Help desks

Related tools and articles

We use Google Analytics cookies only if you accept. Privacy policy