Skip to content
GDPR compliant. Hosted in DE.
14-day free trial →
RFC 9989pct removednp, psd and t
Security 17 min read 29 Oct 2026

DMARC under RFC 9989: what changes in your DNS record

RFC 9989 replaces the old DMARC spec: pct, rf and ri are gone, np, psd and t are new. How to rebuild your DMARC record tag by tag without breaking mail flow.

X

XICTRAQ® Team

XICTRAQ®

DMARCEmail authenticationDNS-Monitoring
3
tags removed: pct, rf and ri (RFC 9989)
8
DNS queries at most per tree walk (RFC 9989)
5,000
emails per day to Gmail accounts: send more and you need DMARC (Google)
Rebuilding the DMARC record for RFC 9989The same TXT record at _dmarc.example.com, before and in two variants afterRFC 9989 · May 2026Beforeper RFC 7489v=DMARC1p=quarantinepct=50rf=afrfri=86400rua=mailto:…Path A: enforceno pct, policy appliesv=DMARC1p=quarantinenp=rejectrua=mailto:…same as pct=100Path B: testt=y plus legacy pct=0v=DMARC1p=quarantinenp=rejectt=ypct=0rua=mailto:…like pct=0pct=50 has no equivalent: either apply the full policy (path A) or go back to test mode (path B)Removedpctpercentage, now trffailure report formatriaggregate report intervalNewnpnon-existent subdomainspsdpublic suffix flagttest mode: y or nt=y: one level milderquarantinenonerejectquarantinereports continue unchangedSource: RFC 9989 (sections 4.7, 4.10, A.6, C.5), IETF, May 2026; previous values per RFC 7489
Since May 2026, DMARC has a new foundation: RFC 9989 replaces the previous specification, RFC 7489 (RFC 9989), and with it changes what belongs in the DNS record at _dmarc. The tags pct, rf and ri are removed; np, psd and t are added. If you set your DMARC record to half strength years ago with pct=50, you are relying on a value that validators following the new standard no longer evaluate. Ahead of the newsletter waves around Black Friday and Black Week, this is a good moment to walk through your own record tag by tag: what can stay, what should go, what is worth adding, and how does the record stay unambiguous for old and new receivers during the transition?

What RFC 9989 changes in DMARC

Until now, DMARC was not an Internet standard in the strict sense. RFC 7489 was published in March 2015 as an informational document (RFC 7489), through the independent submission stream rather than as the output of an IETF working group (RFC 9989). The new version is an IETF Standards Track document. Besides RFC 7489, it also obsoletes RFC 9091, the experimental procedure for DMARC on public suffixes such as country-code domains; according to RFC 9989, that experiment was not implemented as written (RFC 9989).

Reporting has moved into documents of its own. RFC 9990 describes the aggregate reports that receivers send to the address in rua (RFC 9990), and RFC 9991 the failure reports on individual messages sent to the address in ruf (RFC 9991). Both replace the corresponding parts of RFC 7489.

For the record itself there is reassuring news: the version stays v=DMARC1. New tags explicitly do not require a new version, because validators must ignore unknown tags (RFC 9989). An existing record therefore stays valid. It may simply no longer do what its author intended back then, and that is what the following sections are about.

The changes at a glance

Removed: pct (share of messages), rf (failure report format), ri (aggregate report interval) and the size limit after report addresses. New: np (policy for non-existent subdomains), psd (public suffix flag) and t (test mode). Unchanged: v, p, sp, adkim, aspf, rua, ruf and fo (RFC 9989).

The DMARC record tag by tag

A typical record from recent years looks roughly like this: v=DMARC1; p=quarantine; pct=50; rf=afrf; ri=86400; rua=mailto:dmarc@example.com!10m. Almost every element in it is affected by the new version. The table shows what happens to each tag and what that means for your own record.

TagRFC 7489RFC 9989What to do
vDMARC1DMARC1, unchangednothing
pnone, quarantine, rejectunchangedreview the value, see the section on reject
sppolicy for subdomainsonly for existing subdomains nowkeep, add np
nponly in the RFC 9091 experimentnew: non-existent subdomainsadd, often reject
pctinteger from 0 to 100, default 100 (RFC 7489)removed, historic in the registryremove or set to 100
tnot availablenew: y or n, default nonly for test mode
psdnot availablenew: y, n or uusually leave out
rffailure report format, default afrfremovedremove
rireport interval, default 86400 seconds (RFC 7489)removedremove
rua, rufaddresses, optionally with a size limitsize limit obsoleteremove suffixes like !10m
adkim, aspf, foalignment, reporting optionsunchangednothing

Removed does not mean wrong: a record with ri=86400 stays valid; the tag is simply no longer evaluated. With pct, however, the behavior depends on which version the receiving server follows.

pct is gone: why 50 percent did not work reliably

The idea behind pct made sense. RFC 7489 allowed any integer from 0 to 100 (RFC 9989), and domain owners were meant to use it to move gradually from none to quarantine or from quarantine to reject: the stricter treatment would first apply only to a share of the messages that fail DMARC. Many guides from recent years build a staircase of several percentage steps on this, including older posts on this blog such as the guide to analyzing DMARC reports. Its part on reading the reports remains valid; the percentage staircase is outdated with RFC 9989.

Practice looked different. According to the operational experience recorded in RFC 9989, pct was usually applied accurately only for the values 0 and 100; for all other values, the behavior varied widely from one implementation to another (RFC 9989). The value 100 was easy because it was the default and needed no special processing. The value 0, however, took on a meaning nobody had planned: intermediaries such as mailing lists and some mailbox providers treated it as a signal to rewrite the From: header so that forwarded messages would not fail DMARC later (RFC 9989).

RFC 9989 draws the consequence from this experience: pct is removed and replaced by t with the values y and n. Mailbox providers and intermediaries are meant to treat t=y like the former pct=0 and t=n like pct=100 (RFC 9989). In the DMARC tags registry, IANA now lists pct with the status "historic", meaning a tag that is not expected in current implementations (RFC 9989).

pct=50 has no equivalent

A half-set record cannot be translated. t=y corresponds to pct=0, t=n corresponds to pct=100, and the new standard has no intermediate value. If your record currently says pct=50 or any other intermediate value, you have to decide: let the policy apply in full or deliberately go back to test mode.

t=y: test mode with a clear effect

With t=y, the domain owner asks the validating receiver not to apply the published policy yet. The expectation is spelled out precisely: messages that fail DMARC are treated one level more leniently. p=quarantine with t=y effectively becomes none, and p=reject with t=y becomes quarantine (RFC 9989). The tag has no effect on a none policy, and it does not change reporting: aggregate reports keep coming (RFC 9989).

That makes t=y the tool for the phase in which pct=0 used to be set. The domain owner already publishes p=quarantine, but receivers still treat failing messages as with none, and intermediaries with special handling can rewrite the From: header. The aggregate reports then show which paths carry mail that would fail without that rewriting. The example in RFC 9989 describes exactly this case for a test subdomain with p=quarantine and t=y (RFC 9989).

Important in practice: t=y applies to all policy tags in the record, including sp and np (RFC 9989). A record with np=reject and t=y therefore protects non-existent subdomains only at the quarantine level. If you want np=reject fully effective right away, you cannot use test mode in the same record at the same time.

t=y is not a permanent state

A record with t=y signals that the domain owner is still testing. If it stays unchanged for months, it protects the domain one level less than the policy suggests at first glance. Switching to t=n or removing the tag therefore belongs in the plan with a date.

Transition period: serving old and new validators at once

RFC 9989 has been published since May 2026 (RFC 9989), but not every receiving server validates according to the new version from that day on. For a while, old and new implementations work side by side. Both versions specify what happens to tags an implementation does not know: they must be ignored (RFC 7489). A validator that only knows RFC 7489 therefore skips t and psd, and a validator following RFC 9989 no longer evaluates pct, because the new version no longer describes the tag.

This explains why pct=50 is particularly unfortunate during the transition: an old validator applies the policy to at most half of the failing messages, a new one to all of them. The same record behaves differently at two receivers, and aggregate reports become hard to compare. The record only becomes unambiguous once pct is missing or set to 100. Then both generations apply the full policy, because 100 was the default in RFC 7489 (RFC 7489).

Record (excerpt)Validator per RFC 7489Validator per RFC 9989Unambiguous?
p=quarantine; pct=50quarantine for at most halfquarantine for allno
p=quarantine; pct=100quarantine for allquarantine for allyes
p=quarantine (no pct)quarantine for allquarantine for allyes
p=quarantine; t=yquarantine for alltreated like noneno
p=quarantine; pct=0; t=yno quarantine, normal classificationtreated like noneyes, test mode

For test mode, this means: if you also want to reach older validators, you can keep pct=0 next to t=y. The old validator does not know t and reads pct=0; the new one does not evaluate pct and reads t=y. Both end up with treatment one level more lenient, because under RFC 7489, too, messages that do not fall under reject because of pct were to be treated as with quarantine (RFC 7489). As soon as test mode ends, both tags go together.

np: protection for subdomains that do not exist

Anyone forging mail in a brand's name likes to use senders that sound plausible: invoice.example.com, shipping.example.com, service.example.com. If these names do not exist in DNS at all, they used to fall under the subdomain policy sp or, without sp, under the main policy p. With np, the domain owner can set a policy specifically for this case. The tag comes from the RFC 9091 experiment and is now a regular part of the standard (RFC 9989).

A name counts as non-existent if DNS answers with NXDOMAIN; then all names below it do not exist either (RFC 9989). If np is missing, sp applies to such names and, if that is missing too, p (RFC 9989). That makes np one of the few changes that pay off without a long lead time: a subdomain that does not exist in DNS typically sends no legitimate mail. A record that is still monitoring the main domain with p=none can therefore already carry np=reject. The policy only affects messages that fail DMARC anyway.

Before setting it, a look at the aggregate reports is still worthwhile. If a sender subdomain with failing authentication shows up there that actually belongs to one of your own services, such as a ticket system or an invoicing service, set up that service properly first. For domains that send no mail at all, the BSI technical guideline on email authentication already requires a DMARC policy of reject (BSI); np=reject carries this idea over to the names below an active domain.

np

Policy for subdomains without a DNS entry. If the tag is missing, sp applies, otherwise p.

t

Test mode with y or n. t=y replaces the former pct=0 and lowers the treatment by one level.

psd

Public suffix flag: y, n or the default u. Rarely relevant for shops.

psd and the DNS tree walk

The third new tag addresses a small group. With psd=y, operators of public suffixes, such as registries for top-level domains, mark their record; psd=n explicitly declares a name to be the organizational domain for itself and everything below it; the default u leaves the decision to the procedure (RFC 9989). For the record of a shop or company, this usually means: leave psd out.

What lies behind it is more interesting. RFC 7489 determined the organizational domain with the help of a public suffix list but specified neither a particular list nor how often to fetch it. RFC 9989 replaces this with the DNS tree walk: starting from the sender domain, the receiver queries the parent names level by level and determines the organizational domain from the DMARC records it finds (RFC 9989). So that sender domains with very many labels cannot trigger a flood of queries, even very long names lead to at most eight DNS queries (RFC 9989).

For most domains, nothing changes in the result; for complex organizations with deep subdomain trees, it may. RFC 9989 names the remedy itself: different results between the old list logic and the new tree walk can be avoided with strict alignment and explicit records for all sender domains in use (RFC 9989). A record published directly at the sender name is queried before any tree walk and therefore takes precedence.

Replace, do not add

If the receiver finds more than one DMARC record at a name, it discards all of them (RFC 9989). This happens easily during a rebuild: the new record is created and the old one stays. To receivers, the domain then has no DMARC policy until the mistake is fixed. Change or delete the old TXT record in the same step.

rf, ri and the size limit: leftovers that can go

Two more tags are removed without replacement. rf set the format of failure reports, with the default afrf; ri requested an interval between two aggregate reports, with the default of 86400 seconds, which is one day (RFC 7489). Even RFC 7489 only required daily reports; shorter intervals were a best-effort courtesy. RFC 9990 now describes the reporting period without a tag of its own: it typically covers a single UTC day starting at 0000 UTC (RFC 9990).

The size limit after a report address, such as !10m at the end of rua=mailto:dmarc@example.com!10m, also belongs to the old syntax. RFC 9989 lists it only as obsolete syntax that report senders should ignore (RFC 9989). Leaving it in does no harm according to the text; a tidy record is still easier to check.

The report addresses themselves stay as they are. rua remains the basis of every migration: if the tag is missing, receivers must not generate aggregate reports for the domain (RFC 9989). ruf for failure reports on individual messages remains allowed as well. The BSI, however, advises against requesting such reports because they carry personal data (BSI). If your record still has ruf, use the rebuild to make that decision deliberately.

p=reject is not the goal for every domain

The tone around the strictest policy is new as well. RFC 9989 states explicitly that domains whose users might post to mailing lists should not publish p=reject (RFC 9989). The reason is indirect paths such as forwarding, role addresses and lists, where SPF typically fails. If you still want reject, RFC 9989 says you should first monitor with p=none for at least a month, then run p=quarantine for an equally long period and compare the results (RFC 9989).

For domains that publish reject, RFC 9989 also requires valid DKIM signatures rather than relying on SPF alone, because DKIM usually survives forwarding (RFC 9989). And on the receiving side there is a sentence that may surprise many: receivers must not reject messages solely because of p=reject and, without further knowledge, must treat such mail as if the policy were quarantine (RFC 9989).

In its technical guideline TR-03182, the BSI sets a different emphasis: it expects quarantine or reject and names reject as the long-term goal (BSI). The two can be reconciled. For a pure sending domain that a shop uses for order confirmations and newsletters and whose addresses nobody uses for mailing lists, reject is usually well founded. For the domain that hosts the team's personal mailboxes, quarantine is often the more cautious choice. If you display a brand logo via BIMI in the inbox, also check after the rebuild whether the requirements of the respective mailbox provider for the policy are still met.

Rebuilding the DMARC record step by step

The rebuild itself takes minutes; the preparation does not. In this order, the record stays unambiguous in every phase:

  1. Save the current state: query the current record at _dmarc and note it with a date. The free mail server check outputs the published DMARC record verbatim and reports when there is more than one record at _dmarc.
  2. Read the aggregate reports: the reports from recent weeks show which sources still fail and whether legitimate senders show up under missing subdomains.
  3. Decide on pct: if pct is below 100, either enforce in full (remove pct) or deliberately switch to test mode (t=y, plus pct=0 for older validators).
  4. Remove leftovers: delete rf, ri and size limits such as !10m after the report addresses.
  5. Add np: for non-existent subdomains, usually np=reject, provided the reports give no reason against it. In test mode with t=y, it applies one level more leniently.
  6. Replace instead of adding: change the old TXT record in the same step; do not create a second one.
  7. Measure again: after the TTL has expired, query the record again and check in the next aggregate reports whether the new policy is being applied. That exactly one record with the intended policy stays in place is then shown continuously by DNS monitoring.

In the zone file, the rebuild looks like this for the three cases. The address example.com stands for your own domain:

_dmarc.example.com
; before, per RFC 7489
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=quarantine; pct=50; rf=afrf; ri=86400; rua=mailto:dmarc@example.com!10m"

; path A: apply the full policy
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=quarantine; np=reject; rua=mailto:dmarc@example.com"

; path B: test mode, unambiguous for older validators too
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=quarantine; np=reject; t=y; pct=0; rua=mailto:dmarc@example.com"

After saving, a simple query shows what the world actually sees. The number of lines is what matters: if two v=DMARC1 records come back, the old one is still there.

$ dig +short TXT _dmarc.example.com
"v=DMARC1; p=quarantine; np=reject; rua=mailto:dmarc@example.com"

Before Black Week: keep the record in view

Especially before the high-revenue weeks in November, it pays not to leave the rebuild until the last day. The large mailbox providers tie their rules for bulk senders to a published DMARC record: Google requires it from senders who send more than 5,000 messages per day to Gmail accounts (Google), Microsoft, for Outlook, from domains sending more than 5,000 emails per day (Microsoft), and Yahoo from bulk senders with at least p=none (Yahoo). None of the three requires a strict policy; Google, too, accepts none (Google).

The risk therefore lies less in the policy than in the rebuild itself. If you create a second record while editing, mistype v=DMARC1 or accidentally write the record to the wrong name, these receivers see no valid DMARC record, possibly in the middle of a campaign. Which other rules apply to bulk sending is summarized in the post on deliverability rules for bulk email.

In DNS and domain monitoring, XICTRAQ® checks SPF, DKIM and DMARC with every DNS check. For DMARC, the website view shows whether exactly one record is published and which policy it sets; if there are several records at _dmarc, this appears as a warning. Changes to the TXT records of the domain itself, such as the SPF record, are kept in the changelog with old and new value. If email authentication is missing entirely, an alert goes out through the configured notification channels.

The check does not yet evaluate the new tags np, psd and t separately. If you have switched your record to RFC 9989, you can read them in the verbatim record that the mail server check outputs unchanged. Whether an alert really arrives at night is answered better by an alerting drill than by any assumption. And for your own documentation, one simple rule helps: every change to the DMARC record gets a date, the old and new wording and one sentence of justification.

Bring your DMARC record in line with RFC 9989 once and properly

The rebuild is manageable: pct, rf, ri and size limits out, np in, t=y only for a time-limited test phase, and psd usually stays out. More important than any single tag is that the record afterwards means the same thing to old and new validators and exists only once in DNS. As with the HSTS preload list, the same applies here: a record that controls the behavior of other people's servers is checked first and committed second.

If you are setting up DMARC for the first time, start with the basics from the guide to SPF, DKIM and DMARC and write the record according to RFC 9989 right away, that is, without pct. If you have already switched, use the weeks before the holiday season to read the aggregate reports and then let the record run quietly. The live demo shows what the check looks like day to day.

Sources and studies

This post is based on: RFC 9989, Domain-Based Message Authentication, Reporting, and Conformance (DMARC), IETF, May 2026, in particular sections 4.7, 4.8, 4.10, 5.1 and 7.4 and appendices A.6, B.2.5 and C; RFC 9990, DMARC Aggregate Reporting, IETF; RFC 9991, DMARC Failure Reporting, IETF; RFC 7489, DMARC, sections 6.3 and 6.6.4; BSI TR-03182 Email Authentication; Google, email sender guidelines (Gmail); Microsoft, Outlook requirements for high-volume senders; Yahoo, Sender Best Practices. This post describes the state of the standards and does not replace a review of your own mail infrastructure.

Is my old DMARC record invalid under RFC 9989?
Usually not. The version stays v=DMARC1, and validators must ignore tags they do not know (RFC 9989). A record with pct=50, however, acts at validators following the new version like a record without pct, that is, with the full policy. If you do not want that, switch the record deliberately.
What do I use instead of pct=50?
There is no direct equivalent. t=y stands for the former pct=0, and t=n or a missing t for pct=100 (RFC 9989). If the aggregate reports are clean, the policy applies in full without pct. If legitimate sources still fail, test mode with t=y is usually the more honest state.
Should I set np=reject?
For most domains it is a sensible addition, because non-existent subdomains typically send no legitimate mail. If np is missing, sp or p applies to such names (RFC 9989). Beforehand, it is worth checking the aggregate reports for senders under subdomains that are missing in DNS.
Do I need to add psd to my record?
Typically not. psd=y is meant for operators of public suffixes, psd=n for organizations that want to explicitly mark a name as their own organizational domain (RFC 9989). Without the tag, the default u applies, and the receiver determines the organizational domain via the DNS tree walk.
Does t=y affect DMARC reports?
According to RFC 9989, the tag does not change how reports are generated (RFC 9989). During the test phase, the aggregate reports therefore keep showing which sources pass DMARC and which do not. That is exactly what test mode is for.
Does XICTRAQ® recognize the new tags np, psd and t?
The XICTRAQ® DNS check shows whether exactly one DMARC record is published and which policy it sets, and reports missing email authentication as an alert. It does not yet evaluate the new tags separately. The free mail server check shows their wording by outputting the published record unchanged.

Related in XICTRAQ®

Start in 60 seconds

Let XICTRAQ® watch your website.

Permanent free plan available. No credit card, no installation, no hidden fees.