DMARC ties SPF and DKIM to the address your recipients actually see, and tells receiving
servers what to do when neither matches. This page is the full reference on the current
standard: every tag with its real default, the DNS tree walk worked through on a real name,
ten annotated example records, a rollout plan, how to read the reports — and what changed
when RFC 9989 replaced RFC 7489. Check your domain → Building a record?
The DMARC generator writes valid syntax for you.
Current as of RFC 9989, RFC 9990 and RFC 9991 (May 2026) · last reviewed 24 August 2026
What DMARC does — and what it explicitly does not
DMARC ties the one identifier a reader actually sees — the domain in the
From header field, called RFC5322.From and, in DMARC terms, the
Author Domain — to authentication results that SPF and DKIM produce for
entirely different identifiers. RFC 9989 §4.2 is candid about why that field was
chosen: it has been trivially forged throughout the history of email, but of all
the identifiers carried inside a message it is the only one required to be
present. SPF and DKIM authenticate domains; neither of them is, by itself,
connected to the domain your recipient reads. DMARC is the binding between them.
A DMARC pass is a narrow statement. Per RFC 9989 §4.1 and §5.4, it says that the
use of the Author Domain in that message was validated as authorized by the Domain
Owner — nothing more. It carries no explicit or implicit value assertion about the
message or about the Domain Owner, and §4.1 adds that the underlying mechanisms
validate the use of a DNS domain only, never the local-part of any address in the
message. A message from ceo@example.com and one from
attacker-with-your-password@example.com are indistinguishable to DMARC.
The second half of DMARC is reporting, and in practice it is the half that does
the work. Aggregate reports give the Domain Owner insight into all mail streams
using the Author Domain (RFC 9989 §5.3.8) and, per RFC 9990 §3.1, cover messages
that passed authentication as well as those that did not, counted across all
messages received even when their delivery was ultimately blocked by other
filtering. That is how you find the forwarder, the ticketing system, and the
marketing platform nobody told you about, before you move to enforcement.
DMARC detects
Exact-domain spoofing. An unauthorized sender putting your
domain in the From address. RFC 9989 §2.2 states the scope in
exactly these terms: DMARC combats specific forms of exact-domain spoofing.
Mail using your domain with no aligned identifier. If no
Authenticated Identifier exists, or none of them align with the Author Domain,
the message fails the DMARC check (§5.3.5) and your published policy applies.
Your own unauthorized or misconfigured sending paths.
Aggregate reports reveal mail streams using your domain that are unaligned or
unauthenticated; §5.1.6 requires the legitimate ones among them to be fixed
before you publish an enforcement policy.
Every sending path, not only the failing ones. Reports
include passing streams too (RFC 9990 §3.1), which is what makes an inventory
of your legitimate senders possible in the first place.
Which policy a receiver actually applied. When a receiver
deviates from your published policy it should report that fact and its reason
through the aggregate report's PolicyOverride feature (§5.4).
DMARC does not detect
Forged display names. A message from
Example Support <billing@unrelated.example.net> is untouched
by DMARC. RFC 9989 §11.4 names this attack and states plainly that display name
attacks are out of scope; §2.4 lists them among the topics excluded from the
specification.
Look-alike and cousin domains. §2.2 states that DMARC does
not address the use of visually similar domain names. A cousin domain is
a different Author Domain, evaluated against its own policy record, and an
attacker who registers one can publish a passing DMARC configuration for it.
Message content and malware. Content analysis is listed as
out of scope in §2.4. A DMARC pass is not a safety verdict, which is why §5.4
allows a receiver to reject or quarantine a message even when it passes and
encourages receivers to keep their anti-abuse technology in place.
Compromised accounts on your own domain. Mail sent with
stolen credentials through your own infrastructure produces an aligned pass.
DMARC validates the domain, not the local-part (§4.1).
Abuse of a delegated subdomain. Under relaxed alignment,
anyone who controls DNS for a subdomain of your Organizational Domain can
publish an SPF record there and obtain a DMARC pass for the parent — the
scenario worked through in §11.8.
Messages with zero or several From domains.
Such messages are outside the specification's scope (§5.3.1), and §11.5
describes the resulting attack vector and the receiver-side limits that
contain it.
One consequence is routinely misunderstood, including by vendors who sell DMARC
as a switch that stops spoofed mail: p=reject is a request, and the
specification forbids acting on it alone.
RFC 9989 §7.4 is normative and unambiguous: “Mail Receivers MUST NOT
reject incoming messages solely on the basis of a ‘p=reject’ policy by
the sending domain.” It continues: “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’.” The same
requirement appears in the conformance list in §8, and §5.4 states that receivers
SHOULD NOT reject solely because of a published reject policy but should apply
other knowledge and analysis. The reason is interoperability: §7.4 works through
forwarders, role addresses and mailing lists, where legitimate mail fails DMARC
and automated bounce handling can unsubscribe innocent recipients.
The mirror image also holds. §5.4 requires that a discovered policy of
p=none MUST NOT modify existing mail handling processes, which is what
makes monitoring mode safe to deploy on a production domain.
Alignment — the mechanism that actually decides
Most DMARC troubleshooting ends the moment you stop asking “did SPF
pass?” and start asking “which domain passed, and is it aligned?”
Three domain identifiers are in play, and they are frequently three different
domains in the same message.
RFC5322.From — the Author Domain
The domain of the apparent author, extracted from the From header
field (RFC 9989 §3.2.2). The receiver extracts it as the first step of evaluation
and converts a U-label to an A-label before processing; if zero or more than one
domain is present, DMARC validation is not possible and the process terminates
(§5.3.1). This is the only header field DMARC evaluates to determine the Author
Domain — §2.4 lists evaluation of anything other than the RFC5322.From header field
as out of scope.
RFC5321.MailFrom — the SPF Domain
The domain in the SMTP MAIL FROM command, also called the envelope
sender or return path. SPF can validate both the HELO identity and the MAIL FROM
identity, but §3.2.4 and §4.4.2 are explicit: DMARC relies solely on SPF validation
of the MAIL FROM identity. An SPF pass earned on the HELO name contributes nothing
to DMARC. For a null return path, §3.2.4 points to the RFC 7208 §2.4 treatment,
where the identity becomes postmaster at the HELO domain.
DKIM d= — the DKIM Signing Domain
The value of the d tag in a DKIM-Signature header field whose
cryptographic signature validates (§3.2.3, §4.4.1). A message may carry several
signatures; §4.4.1 states that it produces a DMARC pass if any
DKIM-Authenticated Identifier aligns with the Author Domain.
The rule that decides the outcome is short. Per §5.3.5, if one or more of the
Authenticated Identifiers align with the Author Domain, the message passes; if no
Authenticated Identifiers exist, or none of them align, it fails. One aligned
identifier is enough — but an identifier that authenticates without aligning
contributes nothing at all. §4.4.1 and §4.4.2 give the reason: there is no accepted
mechanism by which you can name third parties allowed to sign for you or to use your
domain as MAIL FROM, and any bad actor can publish an SPF record for a domain it
controls or sign a message with its own key. Only alignment makes an authentication
result say something about your domain.
Alignment comes in two modes. Two domains are in relaxed alignment
when they have the same Organizational Domain (§3.2.10.1) and in strict
alignment only when they are identical (§3.2.10.2). The mode is yours to
choose, separately for each mechanism, through the aspf and
adkim tags; §4.7 gives both a default of r, so a record
that mentions neither tag is relaxed on both sides. §4.4 notes that these domain
comparisons are case-insensitive and observes that nearly all Domain Owners have
found relaxed alignment sufficient.
The Organizational Domain comes from the Tree Walk, not from the Public Suffix List
This is where current DMARC departs from what most published guidance still says.
§3.2.14 defines the Organizational Domain as the domain at the top of the namespace
hierarchy that shares administrative authority with the domain in question, and
states that it is determined by applying the algorithm in §4.10 — the DNS Tree Walk.
§4.10 records the history explicitly: RFC 7489 described using a Public Suffix List,
mandated no particular list, gave no refresh guidance, acknowledged the resulting
interoperability problems, and suggested that a more reliable method should replace
it. RFC 9989 is that replacement.
The Tree Walk queries for a DMARC policy record at the starting domain, then
repeatedly strips the left-most label and queries again, capped at eight queries so
that an Author Domain with hundreds of labels cannot be used as a denial-of-service
amplifier (§4.10). §4.10.2 then selects the Organizational Domain from the records
found: a record carrying psd=n settles it immediately; a record other
than the starting one carrying psd=y makes the domain one label below it
the Organizational Domain; otherwise the record found at the name with the fewest
labels wins. If the process determines nothing, the initial target domain is its own
Organizational Domain.
Two shortcuts in §4.10.2 are worth knowing, because they explain why some
configurations are cheap to evaluate: no alignment evaluation is needed at all when
the policy record specifies strict alignment, since a string comparison suffices, and
none is needed when the Author Domain and every Authenticated Identifier are the same
domain and a policy record is published for it — that common domain is then treated
as the Organizational Domain.
One message, four alignment questions
SMTP: MAIL FROM:<bounce@mail.example.com>
Headers:
From: Example News <news@example.com>
DKIM-Signature: v=1; a=rsa-sha256; d=mailer.example.net; s=k1; ...
Assume SPF returns pass for mail.example.com and the signature from
mailer.example.net validates. Both mechanisms report success, and both
identifiers are genuine. The Author Domain is example.com. Assume
further that a DMARC policy record exists at _dmarc.example.com and at
no name below it, so the Tree Walk from mail.example.com ends at
example.com.
Mode
Authenticated Identifier
Author Domain
Aligned?
Why
aspf=r (default)
SPF: mail.example.com
example.com
Yes
The Tree Walk gives both domains the same Organizational Domain,
example.com, which is the definition of relaxed alignment
(§3.2.10.1)
aspf=s
SPF: mail.example.com
example.com
No
Strict alignment requires the two names to be identical (§3.2.10.2); a
subdomain of the Author Domain is not the Author Domain
adkim=r (default)
DKIM: d=mailer.example.net
example.com
No
A Tree Walk from mailer.example.net can only yield an
Organizational Domain within example.net, or
mailer.example.net itself if it finds no record — never
example.com
adkim=s
DKIM: d=mailer.example.net
example.com
No
Not identical, and no Organizational Domain comparison is performed in
strict mode
With the defaults, this message passes DMARC: the SPF identifier aligns, and
§5.3.5 needs one. Change the record to aspf=s and the same message
fails, because the DKIM identifier never aligned and the SPF identifier no longer
does. Nothing about the message changed; only the comparison did.
The DKIM row is the one that surprises people. A valid
signature from your email provider's own domain is a valid signature — it
authenticates the provider, not you. Alignment, not validity, is what DMARC
consumes.
Because relaxed alignment is decided by a lookup rather than by a string
comparison, its result depends on where DMARC records exist in the tree.
§11.8 works through a case in which a receiver that determines the Organizational
Domain incorrectly lets an attacker's mail pass, and concludes that the issue is
“entirely avoided by the use of strict alignment and publishing explicit DMARC
Policy Records for all Author Domains used in an organization's email.” The
same section warns against delegating control of subdomains while relying on relaxed
alignment.
Why SPF passes and DMARC still fails
This is the single most common misreading of a DMARC report. SPF says pass, the
Authentication-Results header says spf=pass, and DMARC says fail. Nothing
is broken; the two mechanisms are answering different questions. SPF answers
“was this host allowed to use that envelope sender domain?” DMARC answers
“did any authenticated domain match the domain in the From
header?”
Case 1: an email service provider with its own bounce domain
Three lines, three domains. The SPF-Authenticated Identifier is
esp.example.net, because §3.2.4 and §4.4.2 define it as the validated
MAIL FROM identity — not as the domain you wrote in the From field.
esp.example.net and example.com share no Organizational
Domain, so they are unaligned under relaxed alignment and obviously unaligned under
strict. The DKIM identifier is unaligned for the same reason. No Authenticated
Identifier aligns, so §5.3.5 produces a fail, and your published policy is applied
to mail you sent yourself.
The fix is on the sending side and is spelled out in §5.1.1 and §5.1.2: send mail
whose MAIL FROM domain produces an aligned SPF-Authenticated Identifier — a custom
return path under example.com — and configure the provider to sign with
a DKIM Signing Domain that aligns, typically by publishing or delegating a selector
under your own domain so the signature carries
d=example.com or a subdomain of it.
Adding the provider's include: to your SPF record
does not help here, because SPF never evaluates your domain in this transaction.
Your record is not consulted at all; the envelope sender belongs to the provider.
Case 2: forwarding breaks SPF, DKIM survives
example.com --> alumni.example.org --> final mailbox
(relays the message on, From: unchanged)
RFC 9989 §7.4 walks through precisely this scenario. When the relayed message
arrives at the final destination, the connecting IP address belongs to the
forwarder, not to a host authorized by the original MAIL FROM domain, so the message
“will most likely fail SPF checks unless the RFC5321.MailFrom address is
rewritten by the relaying MTA” — and if it is rewritten, the resulting SPF
pass belongs to the forwarder's domain and is therefore unaligned. The same section
notes the asymmetry that saves the message: “DKIM signatures will generally
remain valid in these relay situations.”
This is why §7.4 states as a critical requirement that domains publishing
p=reject “MUST NOT rely solely on SPF to secure a DMARC pass and
MUST apply valid DKIM signatures to their messages”, a requirement repeated in
the conformance list in §8. A domain at enforcement with SPF as its only aligned
identifier will lose legitimate mail to every forwarder in the path.
§7.1 adds a trap that hides the evidence: with an SPF record
terminating in -all, a receiver may reject early in the SMTP
transaction, before DMARC processing and before the DATA phase. The
From domain is never revealed, its policy is never discovered, and the
message never appears in your aggregate reports — even though an aligned DKIM
signature would have produced a DMARC pass.
Both cases are measurable rather than theoretical: send one real message through
the path you are asking about and read the identifiers off the wire. That is what
the sender test does — it captures a message you send,
reports the envelope sender, every d= in the message, the SPF and DKIM
results, and whether each identifier aligns with your Author Domain.
Where the record lives and how it is read
A DMARC policy is a single DNS TXT record published at the name _dmarc prefixed to the domain it applies to. For example.com that name is _dmarc.example.com, and a receiver looking for the policy of an author domain issues a TXT query for exactly that name (RFC 9989 §4.5). There is no other place a receiver looks: not the apex TXT record, not a differently named label, and not a DMARC-specific resource record type. The query is always for TXT at _dmarc prefixed to the domain in question. The _dmarc node name is registered with IANA for the TXT type in the "Underscored and Globally Scoped DNS Node Names" registry (RFC 9989 §9.5).
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"
Publishing no record at all is a valid position, and RFC 9989 §4.5 says so explicitly: a domain owner opts out of DMARC validation by receivers simply by not publishing a policy record. What matters for you is that the absence of a record and the presence of a record that receivers discard are, in practice, the same outcome — and the second case is far easier to reach by accident. The rest of this section is about the ways a record that looks correct is thrown away.
A TXT record split into several strings is joined before parsing
A TXT record can consist of more than one character-string object. Where that is the case, the module performing DMARC evaluation MUST concatenate those strings by joining the objects in order and parsing the result as a single string (RFC 9989 §4.5). The joining is a plain concatenation — nothing is inserted at the seam.
This matters because DNS hosting interfaces differ in how they present a long value. Some accept one long string and split it for you; some expect you to enter the parts yourself. If a part ends where the next begins, the result parses cleanly. If you add a space or a semicolon at a boundary "to be safe", that character becomes part of the record value, and you may end up with a tag value you did not intend.
Published as
Parsed as
Result
"v=DMARC1; p=reject; rua=mailto:a@" "example.com"
v=DMARC1; p=reject; rua=mailto:a@example.com
Intended. The seam falls inside a value and the concatenation restores it.
The stray space is inside the reporting URI. The URI is not the address you meant.
Check the record as a resolver returns it, not as your DNS panel displays it. The panel shows the parts; the receiver sees the join.
v=DMARC1 must be the first tag, and the value is case sensitive
_dmarc.example.com. IN TXT "v=dmarc1; p=reject"
RFC 9989 §4.7 defines v as REQUIRED, states that it MUST be the first tag in the list, and states that the tag value is case sensitive with DMARC1 as the only possible value. If the tag is not first, is absent, or has any other value, the entire record MUST be ignored. The formal grammar in §4.8 repeats this as a case-sensitive literal: dmarc-version = "v" equals %s"DMARC1".
The record above is not a weak policy and not a partially valid record. It is no record. Every receiver discards it, the domain has no policy of its own, and mail claiming to be from example.com is treated exactly as if the DNS name _dmarc.example.com did not exist. Nothing in the DNS reports an error, and the record will keep looking present in your zone file for years.
The same applies to any record whose first tag is something else. A p=reject; v=DMARC1 ordering is discarded, and so is a record in which a vendor verification string ended up concatenated in front of the policy: RFC 9989 §4.10 step 2 discards records that do not start with a v tag identifying the current version of DMARC. The version tag is the gate, and everything after it is reached only if the gate opens.
Unknown tags are ignored, and broken values fall back to defaults
Only tags defined in the DMARC tag registry are to be processed, and unknown tags MUST be ignored (RFC 9989 §4.7). The formal definition in §4.8 repeats the rule and adds what happens to the rest: syntax errors in the remainder of the record MUST be discarded in favour of default values, if any, or ignored outright. §4.8 also explains the reason — because unknown tags must be ignored, a new tag can be registered without a new DMARC version and without breaking older implementations.
The consequence for you is that a misspelled tag name does not produce an error anywhere. It produces silence. v=DMARC1; poIicy=reject — with a capital I in place of the l — is a syntactically valid DMARC record containing one unknown tag and no policy tag.
What happens then is set out in RFC 9989 §4.10.1. If a retrieved policy record does not contain a valid p tag, or contains an sp or np tag that is not valid, then: if a rua tag is present with at least one syntactically valid reporting URI, the receiver MUST act as if a record containing p=none was retrieved and continue processing; otherwise the receiver applies no DMARC processing to the message at all. A typo in the policy tag therefore costs you either your enforcement or your DMARC evaluation entirely, depending on whether you happen to have a reporting address published.
Two records at the same name discard each other — but the tree walk continues
A TXT query for _dmarc.example.com returns a set of records, and that set may legitimately contain records that have nothing to do with DMARC. RFC 9989 §4.10 step 2 handles both cases in order: records that do not start with a v tag identifying the current version of DMARC are discarded, and if multiple DMARC policy records are returned for a single target, they are all discarded. A vendor verification string sitting alongside your policy at the same name is harmless, because it is removed by the first half of the rule. A second record beginning with v=DMARC1 is not harmless, because it triggers the second half and takes your real policy down with it.
What happens next is where RFC 9989 differs from the older RFC 7489, and the difference is widely misreported. Under RFC 7489 §6.6.3 step 5, a set containing multiple records meant that policy discovery terminated and DMARC processing was not applied to the message — DMARC was, for that message, off. Under RFC 9989 the discard is local to that one name. Policy discovery falls through to the DNS tree walk (§4.10.1): if no valid record is found by the first query, the receiver walks upward, querying _dmarc at each successive parent — for an author domain of more than eight labels the walk first jumps to the ancestor that has seven labels (§4.10 step 4) — and steps 5 to 7 of §4.10 repeat the query-and-discard until the process stops or no labels remain.
So a duplicate record on mail.example.com does not switch DMARC off for that subdomain. It removes the subdomain's own policy and hands the decision to the next record found up the tree — typically the organizational domain's record, whose sp or np tag then applies instead of the p you published on the subdomain. The observable result is not "no DMARC". It is a different policy than the one you wrote, applied for reasons that are invisible from the subdomain's own record.
The tree walk has two stopping conditions worth knowing. It stops when a single remaining record carries a psd=n or psd=y tag (RFC 9989 §4.10 steps 2 and 6), and it is bounded so that an author domain with more than eight labels never causes more than eight DNS queries (§4.10). The eight-query bound is a denial-of-service guard, not a policy feature: §4.10 notes that domains with up to seven labels were observed in use, and eight was chosen to leave room.
The complete tag reference (RFC 9989)
RFC 9989 §4.7 defines eleven valid DMARC tags and nothing else. The table below reproduces the status and the default of each one as the specification states them, because both are routinely misquoted: exactly one tag is REQUIRED, exactly one more is RECOMMENDED, and the remaining nine are OPTIONAL with defaults that decide what an incomplete record means.
Tag
Status
Default
Allowed values
What it does
v
REQUIRED
none — must be present
DMARC1 (case sensitive)
Identifies the record as a DMARC policy record. MUST be the first tag in the list. If it is absent, not first, or has any other value, the entire record MUST be ignored.
p
RECOMMENDED, not required
no default value; an otherwise valid record without p is treated as if it included p=none
none, quarantine, reject
The domain owner assessment policy for mail using the queried domain that does not pass DMARC validation. Applies to the domain queried and to its subdomains unless sp or np says otherwise. Not applicable to third-party reporting records.
sp
OPTIONAL
falls back to p
none, quarantine, reject
Policy for existing subdomains of the prevailing organizational domain, not for the organizational domain itself. Ignored on records published on subdomains of organizational domains and PSDs, as a consequence of how policy discovery works.
np
OPTIONAL
falls back to sp if present, otherwise to p
none, quarantine, reject
Policy for non-existent subdomains of the prevailing organizational domain. Applies only to non-existent subdomains — not to existing subdomains and not to the domain itself.
adkim
OPTIONAL
r
r (relaxed), s (strict)
Whether DKIM identifier alignment is evaluated in strict or relaxed mode.
aspf
OPTIONAL
r
r (relaxed), s (strict)
Whether SPF identifier alignment is evaluated in strict or relaxed mode.
rua
OPTIONAL
no default; absent means no reports
comma-separated list of URIs
Where aggregate feedback reports are sent. Any valid URI can be specified, and a receiver that sends aggregate reports MUST support mailto:. If the tag is not provided, receivers MUST NOT generate aggregate reports for the domain. URI schemes a receiver does not support MUST be ignored.
ruf
OPTIONAL
no default; absent means no reports
comma-separated list of URIs
Where message-specific failure reports are sent. mailto: MUST be supported by any receiver that sends failure reports. If the tag is not provided, receivers MUST NOT generate failure reports for the domain. Unsupported schemes MUST be ignored.
fo
OPTIONAL
0
0, 1, d, s — colon-separated; 0 and 1 are mutually exclusive; d and s may each appear at most once
Requests which failure conditions should produce a failure report. 0 reports when all underlying mechanisms fail to produce an aligned pass; 1 reports when any mechanism fails to produce an aligned pass; d requests a DKIM failure report for a signature that failed evaluation regardless of alignment; s requests an SPF failure report for a failed SPF evaluation regardless of alignment. The content of this tag MUST be ignored if no ruf tag is also specified.
psd
OPTIONAL
u
y, n, u
Flags whether the domain is a public suffix domain. y is published by a PSO and is used during policy discovery to determine the organizational domain and the DMARC policy domain. n asserts that the domain is not a PSD but is the organizational domain for itself and its subdomains. u, the default, likewise asserts that the domain is not a PSD, but leaves open whether it is the organizational domain for itself and its subdomains — that is left to the tree walk.
t
OPTIONAL
n
y, n
Policy test mode. y asks the validator not to apply the declared policy but to apply any special handling rules it has in place, such as rewriting the From header field; the domain owner expects failing messages to receive one level below the declared policy — quarantine becomes none, reject becomes quarantine — irrespective of any such special handling. n, the default, asks for the declared policy to be applied as written. The tag does not affect report generation and has no effect on a policy of none.
Four of those rows describe behaviour that a table cell states but a reader tends to skip. They are the ones that most often turn a record into something other than what it looks like.
p is recommended, not required
RFC 9989 §4.7 marks p as RECOMMENDED for DMARC policy records, and states that an otherwise syntactically valid record without it is treated as if it included p=none. A record of v=DMARC1; rua=mailto:dmarc@example.com is therefore valid, is not a syntax error, and behaves as monitoring. §4.7 also notes that p is not applicable to third-party reporting records, which is why such records legitimately carry no policy at all.
fo without ruf does nothing
§4.7 states that the content of the fo tag MUST be ignored if a ruf tag is not also specified. v=DMARC1; p=reject; fo=1 is a record whose fo value no conforming receiver will act on, because there is nowhere to send the report. The default is 0, which only matters once ruf exists.
sp only works where it is read
§4.7 notes that sp is ignored for records published on subdomains of organizational domains and of PSDs, because of how policy discovery in §4.10.1 works: a record found at the author domain supplies its policy from p, and sp is consulted only when the applicable record was found further up the tree. Publishing sp=reject on mail.example.com to govern a.mail.example.com is a common construction that has no effect.
np is narrower than it looks
§4.7 restricts np to non-existent subdomains of the organizational domain queried — not to existing subdomains, and not to the domain itself. It is the tag for the parked-name attack surface, not a stricter version of sp. Absent np, the policy from sp applies to non-existent subdomains if sp is present, and otherwise the policy from p.
One further consequence of §4.7 that no table cell holds well: only tags in the registry are processed, and unknown tags MUST be ignored. Every tag you have seen in the wild that is not in the table above — vendor prefixes, comment tags, invented options — is discarded by conforming receivers before the policy is evaluated.
Removed and historic tags
The tags below are not deprecated-but-honoured. RFC 9989 §C.5.2 lists them as removed from the specification, and the IANA DMARC tags registry in §9.3 carries them with status historic, which §9.3 defines as deprecated and not expected to be in use in any current implementation. They are not among the eleven valid tags in §4.7, so a receiver implementing RFC 9989 has no defined handling for them. Whether they still do anything for you depends entirely on how many of your recipients' mail systems are still running the older RFC 7489 behaviour — which is not something you can observe, control, or plan around.
Tag
What it did
Status since RFC 9989
What to do instead
pct
Requested that the policy be applied to only a percentage of failing messages. RFC 7489 defined it as an integer from 0 to 100 inclusive, default 100.
Removed (§C.5.2); registry status historic (§9.3)
For pct=0 only, use t=y. For any other value there is no successor — see below.
rf
Requested the format of failure reports. RFC 7489 defined it as a colon-separated list with default afrf, and noted that afrf was the only format supported in that version.
Removed (§C.5.2); registry status historic (§9.3)
Publish nothing. afrf remains the single entry in the DMARC report formats registry with status active (RFC 9989 §9.4), and the failure report format is defined by RFC 9991, which updates the reporting format described in RFC 6591 (RFC 9991 §4).
ri
Requested an interval between aggregate reports in seconds. RFC 7489 defined it as a 32-bit unsigned integer with default 86400, and treated anything other than a daily report as best effort.
Removed (§C.5.2); registry status historic (§9.3)
Publish nothing. RFC 9990 §3.1 describes aggregate feedback as daily or more frequent reports, and defines no tag by which a domain owner requests a different interval.
!size suffix on a reporting URI rua=mailto:d@example.com!10m
Appended a maximum report size to a single reporting URI. RFC 7489 §6.2 defined the syntax as an exclamation point followed by a maximum-size indication, and §6.4 defined the units as powers of two, so !10m meant ten times 2^20 bytes.
Obsolete syntax. The grammar in RFC 9989 §4.8 still parses it, as obs-dmarc-uri and obs-dmarc-report-size, but the accompanying note states that reporters should ignore the size if they find it in a policy record.
Remove the suffix. RFC 9990 §3.5.2 puts the size problem on the report generator instead: the aggregate XML SHOULD be gzip-compressed, because declining to compress can push the message past the receiving SMTP size limit. Note that an exclamation point that is genuinely part of a URI MUST be encoded (RFC 9989 §4.8).
What t=y actually replaced, and what it did not
The single most common error in DMARC documentation is the claim that t=y is the modern way to do a percentage rollout. It is not. RFC 9989 §C.5.1 registers t with the description "Replacement for some 'pct' tag functionality", and Appendix A.6 sets out precisely which part.
According to Appendix A.6, operational experience showed that pct was usually not applied accurately unless the value was 0 or 100, and that the inaccuracies at other values varied widely from one implementation to another. Value 100 was the default and needed no special processing. Value 0 had acquired an unintended second meaning: some intermediaries and mailbox providers used it as a signal to deviate from standard handling, usually by rewriting the From header field to avoid downstream DMARC failures. That behaviour turned out to be genuinely useful, because comparing aggregate reports before and after publishing pct=0 let a domain owner infer how much of its traffic passed through intermediaries that do not rewrite the From header field. Appendix A.6 concludes that it made no sense to keep a tag named pct that had only two useful values, and introduces t with values y and n as analogous in application to the pct values 0 and 100 respectively.
Read the scope of that carefully. The two endpoints were carried forward. The middle was not. There is no tag in RFC 9989 that expresses pct=50, no combination of the eleven valid tags that approximates it, and no statement anywhere in the specification that a partial rollout remains possible by other means. A staged rollout under RFC 9989 moves through policy levels — none, then quarantine, then reject, optionally with t=y to hold each new level one step below while you read the reports — not through fractions of your mail stream.
If your record still carries a pct tag
Removing it is correct, but the order of operations matters, because your recipients are not all running the same version of DMARC.
A receiver implementing RFC 9989 has no defined handling for pct: it is not one of the eleven valid tags in §4.7, and the registry marks it historic (§9.3). For that receiver your record already reads as p=reject flat, applied to every failing message. A receiver still implementing RFC 7489 reads the tag and applies your policy to the requested share. So a record such as v=DMARC1; p=reject; pct=25 is not a 25 percent rollout. It is full enforcement at every updated receiver and a quarter enforcement at the rest, with the split determined by other people's upgrade schedules.
Deleting the tag closes that gap by moving the second group to the first group's behaviour: RFC 7489 gave pct a default of 100, so a legacy receiver that no longer sees the tag falls back to applying your policy to all failing mail. The change is instantaneous and it is not gradual. If your ramp was real — if pct=25 was protecting a mail stream you had not finished authenticating — then deleting the tag is the moment that protection ends for the legacy portion of your recipients.
The sequence that avoids a surprise is therefore: read your aggregate reports first and confirm that the legitimate sources you know about are producing aligned passes; treat the updated receivers as the ground truth, since they are already enforcing at full rate and their reports show you what full enforcement costs; then remove pct. If the reports show sources you have not fixed yet, the answer is to drop p back to quarantine or none, or to set t=y, and to leave the policy level doing the work. Those mechanisms are defined, uniform across receivers, and visible in the reports. A percentage is none of those things any more.
The one clean translation is pct=0 → t=y, and it is the only one Appendix A.6 preserved — but across the two receiver populations it is not a like-for-like swap. On an RFC 9989 receiver the record moves down one level: pct is an unknown tag there, so your p was applying in full, and t=y asks for one level below it (§4.7). On an RFC 7489 receiver it moves the other way, up one level: under §6.6.4 mail not selected by pct is treated as quarantine where your policy is reject, and left to normal local classification where it is quarantine — after the swap t is an unknown tag there too, pct falls back to its default of 100, and your p applies in full.
The DNS tree walk: which policy actually applies
Before a receiver can apply any policy to a message, it has to decide which DMARC record governs that message. RFC 9989 answers that with an algorithm called the DNS tree walk (Section 4.10). It replaces the Public Suffix List lookup of RFC 7489, and for domains that publish records at more than one level it can produce a different answer than the list did.
The same walk answers two different questions. Policy discovery (Section 4.10.1) asks which record applies to the Author Domain. Identifier alignment evaluation (Section 4.10.2) asks which organizational domain the Author Domain and each authenticated identifier belong to, so that relaxed alignment can be decided. The mechanics are identical; only the starting name differs, and Section 4.10.2 lists the possible starting points: the Author Domain, the SPF-authenticated identifier if SPF passed, and any DKIM-authenticated identifier that passed.
What the receiver actually queries
Section 4.10 states the procedure as seven steps. In the wording of the RFC, with the DMARC-specific parts spelled out:
Query the DNS for a TXT record matching the format of a DMARC policy record at the starting name. For policy discovery, that starting name is the immediate parent domain of the Author Domain with the label _dmarc prepended when the Author Domain has eight or fewer labels, and otherwise the name produced by the shortening in step 3; the query at the Author Domain itself comes before the walk begins (Section 4.10.1). A possibly empty set of records comes back.
Discard every record that does not start with a v tag identifying the current version of DMARC. If more than one DMARC policy record is returned for a single name, discard all of them. If a single record remains and it contains psd=n or psd=y, stop.
Break the subject name into ordered labels, assign the label count to x, and number the labels from right to left. For a.mail.example.com, x is 4, com is label 1, example is label 2, mail is label 3.
If x < 8, remove the leftmost (highest-numbered) label. If x >= 8, remove leftmost labels until seven labels remain. The result is the next name to query.
Query the DNS for a DMARC policy record at that name.
Apply the same discard and stop test as step 2.
Remove the leftmost label from the previous target and repeat steps 5 to 7 until the walk stops or no labels remain.
Nothing in that procedure stops at a registry boundary. If no psd tag is encountered, the walk runs off the top of the tree: the last name queried is the single-label top-level domain, for example _dmarc.com. That is deliberate, because a public suffix operator is allowed to publish a record and the walk has to be able to find it.
A duplicate _dmarc TXT record is not a tie that one side wins. Step 2 and step 6 of Section 4.10 say that if multiple DMARC policy records are returned for a single name, they are all discarded. The receiver then behaves as if that name had no record and keeps walking upward, so a leftover record from a migration can hand your subdomains to a policy published above you. Unrelated TXT records at the same name are harmless: they are discarded first, because they do not start with the v tag.
The eight-query limit and the label shortcut
Section 4.10 explains why the limit exists. Because the walk is a series of DNS queries driven by a name the sender controls, an ill-intentioned domain owner could send mail with Author Domains of tens or hundreds of labels to mount a denial-of-service attack on the receiver. The shortcut in step 4 is the guard: Author Domains with more than eight labels do not result in more than eight DNS queries. Eight was chosen because observed data at the time of publication showed Author Domains with up to seven labels in use, leaving room for expansion.
The exact rule is the one in step 4, and it is a jump, not a decrement. When the subject name has eight or more labels, the receiver does not remove one label; it removes labels until exactly seven remain. Every name between the Author Domain and that seven-label name is skipped and never queried. Section 4.10.1 states the same rule from the other side: if the Author Domain has eight or fewer labels, the walk starts at its immediate parent; otherwise it starts at the name produced by the shortening in step 3 onward.
The record that is never seen
Author Domain: mail.a.b.c.d.e.f.g.example.com (10 labels)
Record with psd=n published at: b.c.d.e.f.g.example.com ( 8 labels)
Queries the receiver makes:
_dmarc.mail.a.b.c.d.e.f.g.example.com
_dmarc.c.d.e.f.g.example.com <- jump straight to seven labels
_dmarc.d.e.f.g.example.com
_dmarc.e.f.g.example.com
_dmarc.f.g.example.com
_dmarc.g.example.com
_dmarc.example.com
_dmarc.com
This is the example from Section 5.1.8, which describes exactly this configuration for large organizations with decentralized DNS management: an apex record at example.com, a department declaring its own organizational domain with psd=n at a zone cut, and an Author Domain below that. Two names are skipped, a.b.c.d.e.f.g.example.com and b.c.d.e.f.g.example.com, and the department's record is one of them.
The record at the zone cut will never be discovered. Section 5.1.8 gives the remedy as a MUST: a domain owner who wants a specific DMARC record applied to an Author Domain longer than eight labels must publish a record at that Author Domain itself, because that name is always queried before the walk begins.
psd=y and psd=n as stop conditions
The psd tag is the only thing that can stop a walk early. Section 4.7 defines three values. psd=y is published by a public suffix operator to declare that the name is a public suffix domain. psd=n declares that the name is not a public suffix domain but is the organizational domain for itself and its subdomains. psd=u is the default and means the walk decides.
Once the walk has finished, Section 4.10.2 selects the organizational domain from the names that returned valid records, examining them from the longest to the shortest:
A record containing psd=n makes that name the organizational domain, and selection ends.
A record containing psd=y at a name other than the one the walk started at makes the organizational domain the name one label below it in the DNS hierarchy, and selection ends. For names within the eight-query budget, that is the name the walk queried immediately before.
Otherwise the record found at the name with the fewest labels wins, and that name is the organizational domain.
If none of the three determines an answer, the initial target domain is the organizational domain. Note what rule 3 means for a record sitting between the Author Domain and the apex: without a psd tag it does not win merely by being closer to the Author Domain. Appendix B.4.1 spells this out with records at both signing.example.com and example.com, where example.com is the organizational domain because it has the fewest labels.
Worked example: mail.eu.corp.example.co.uk
Take a message whose Author Domain is mail.eu.corp.example.co.uk. That is six labels, so the eight-query shortcut never engages and the walk removes one label at a time. Assume this DNS state:
This is the first query of policy discovery (Section 4.10.1), not part of the walk. Nothing is found, so a walk is needed. The Author Domain has six labels, so the walk starts at its immediate parent.
2
_dmarc.eu.corp.example.co.uk
No DMARC record
Empty set, nothing to keep. Remove the leftmost label and query again.
3
_dmarc.corp.example.co.uk
v=DMARC1; p=none; rua=...
A single valid record, kept as a candidate. No psd tag, so the default u applies and the walk does not stop here.
4
_dmarc.example.co.uk
v=DMARC1; p=reject; sp=quarantine; rua=...
A second candidate, with fewer labels than the one from query 3. Still no psd tag, so the walk continues.
5
_dmarc.co.uk
v=DMARC1; p=none; psd=y; rua=...
A single valid record carrying psd=y. Step 6 of Section 4.10 stops the walk here. _dmarc.uk is never queried. Five queries in total.
Which name is the organizational domain. Section 4.10.2, examining the names that returned records from longest to shortest: corp.example.co.uk has no psd tag, example.co.uk has no psd tag, and co.uk has psd=y and is not the name the walk started at. Rule 2 applies, and the organizational domain is the name one label below co.uk on this path: example.co.uk.
Which record governs. Section 4.10.1 ranks the candidates by preference: the Author Domain, then the organizational domain of the Author Domain, then the PSD of the Author Domain. There is no record at the Author Domain, so the record at example.co.uk is the one applied. The Author Domain is a subdomain of it and exists in the DNS, so the assessment policy comes from that record's sp tag: quarantine. If the Author Domain did not exist, np would be used instead; with neither tag present, p would apply to subdomains.
Which records lose. Two of the three published records do not decide this message. The p=reject at example.co.uk is not the applied policy, because sp takes precedence for subdomains of the organizational domain. And the record at corp.example.co.uk plays no part at all: it is neither at the Author Domain nor at the organizational domain, so neither its policy nor its reporting address is used for this message. The DMARC Policy Domain here (Section 3.2.5) is example.co.uk, and that is the name a receiver reports back in the aggregate report.
The same message with psd=n one level down
Change one thing: the team that runs corp.example.co.uk adds psd=n to its record. Nothing else in the zone changes.
#
Name queried
Result
Consequence
1
_dmarc.mail.eu.corp.example.co.uk
No DMARC record
Walk starts at the immediate parent.
2
_dmarc.eu.corp.example.co.uk
No DMARC record
Remove one label, query again.
3
_dmarc.corp.example.co.uk
v=DMARC1; p=none; psd=n; rua=...
A single valid record carrying psd=n. The walk stops. Rule 1 of Section 4.10.2 makes corp.example.co.uk the organizational domain, so this record is the one applied and, with no sp tag present, its p=none governs the Author Domain.
Three queries instead of five, and the outcome for the message flips from quarantine to none. The apex record at example.co.uk and the PSD record at co.uk are never read. One tag on one record moved the administrative boundary one level down the tree, which is precisely the decentralized management case Section 5.1.8 describes.
When the governing record has an invalid policy
Finding a record is not the same as having a policy. Section 4.10.1 defines what happens when the record that would govern the message is syntactically present but its policy tags are not usable, and the rule has a hard edge.
If a retrieved DMARC policy record does not contain a valid p tag, or contains an sp or np tag that is not valid, then:
If a rua tag is present and contains at least one syntactically valid reporting URI, the receiver MUST act as if a record containing p=none had been retrieved, and continue processing.
Otherwise, the receiver applies no DMARC processing to the message at all.
A typo in sp= is therefore not a local failure that falls back to p. On a record that also carries no valid rua URI, it switches DMARC off for that message: no evaluation, no policy, no report. The practical consequence is that rua is not only a reporting feature, it is the condition under which a malformed record still gets you monitoring instead of silence.
Two neighbouring rules complete the picture. Section 4.7 says that if the p tag is absent from an otherwise syntactically valid record, the record is treated as if it included p=none, and points at Section 4.10.1 for the details; publishing p explicitly and always carrying a valid rua keeps you clear of the distinction. And if the walk produces no DMARC record at all, meaning a definite answer rather than a transient DNS error, Section 4.10.1 says receivers MUST NOT apply the DMARC mechanism to the message. Section 5.3.2 states the same conclusion from the receiver's side: if no record is discovered, DMARC validation terminates for that message.
Transient DNS errors are explicitly left to the receiver. Section 4.10.1 offers both options without preferring either: deliver the message to minimise disruption, or reply with an SMTP 4yx code so the sending MTA retries once a definite conclusion can be reached. You cannot rely on one behaviour, which is an argument for keeping the _dmarc names of your whole tree resolvable and fast.
PSL versus tree walk: what changes for you
Section 4.10 is unusually explicit about why the Public Suffix List was dropped. RFC 7489 defined the organizational domain as the domain registered with a registrar and described using a PSL to find it, but it mandated no specific list for receivers to use and gave no guidance on how often a receiver should refresh its copy. It acknowledged that receivers choosing different lists could cause interoperability problems, and it suggested that a more reliable and secure method should replace the PSL if one could be created. Appendix A.5 adds the second motive: climbing the tree without a limit invites a denial-of-service attack through Author Domains full of nonsense labels, which is what the eight-query cap addresses.
Your organizational domain is now something you publish
Section 3.2.14 no longer defines the organizational domain by registration. It defines it as the domain at the top of the namespace hierarchy for a name while having the same administrative authority as that name, and states that it is determined by applying the algorithm in Section 4.10. The boundary is now an outcome of where DMARC records exist, not an entry on a list you do not control.
For the common case the two methods agree. If your apex publishes a record, nothing between the apex and your sending names publishes one, and no PSD above you publishes one, the walk lands on your apex, which is what the PSL would have returned. The answers diverge exactly where records exist at other levels, and that is where the rest of this section matters.
What the tree walk puts in your hands
The organizational domain of any of your names is a function of records you publish, not of a third-party list (Sections 3.2.14 and 4.10).
A department can declare any node in its subtree as its own organizational domain with psd=n, in order to run different policy settings or different reporting URIs than the apex record (Section 5.1.8).
If a PSD above you publishes a record without the appropriate psd=y tag, you can stop a receiver from mistaking it for your organizational domain by adding psd=n to your own record (Section 11.8).
Discovery is bounded and predictable: at most eight queries, whatever the sender puts in the From domain (Section 4.10).
What it does not do
It does not let a record deeper in the tree beat the apex by proximity. Without psd=n, the record with the fewest labels wins (Section 4.10.2, rule 3; Appendix B.4.1).
It does not reach past the query budget. A record at a name the shortcut skips is never discovered, and Section 5.1.8 makes publishing at the Author Domain itself a MUST in that case.
It does not make sp work on a subdomain record. Section 4.7 says sp is ignored on records published on subdomains of organizational domains and PSDs.
It does not remove receiver discretion on DNS errors. Transient failures may be treated as fail open or fail closed (Section 4.10.1).
sp applies at the organizational domain and is ignored below it
Section 4.7 defines sp as the assessment policy for existing subdomains of the given organizational domain, applying only to existing subdomains of the message's organizational domain and not to the organizational domain itself. The same tag definition then names the operational consequence directly: sp is ignored for DMARC records published on subdomains of organizational domains and PSDs, because of the effect of policy discovery in Section 4.10.1.
Two cases make this concrete, assuming example.com is the organizational domain and corp.example.com also publishes a record:
Author Domain of the message
Record applied
Policy taken from
corp.example.com
The record at corp.example.com, found by the first query of policy discovery
Its p tag. Section 4.10.1: when the record applied is that of the Author Domain, the policy comes from p. Its own sp is not consulted.
mail.corp.example.com
The record at example.com, because that is the organizational domain
The sp tag of the example.com record, or np if the Author Domain does not exist, or p if neither is present. The sp at corp.example.com is not consulted.
An sp tag on corp.example.com only ever does anything if corp.example.com is itself the organizational domain, which under Section 4.10.2 means either that it carries psd=n or that it is the shortest name in the walk that returned a record. Publishing sp=reject on a subdomain and expecting it to protect the names below it is the most common way to build a policy that is never applied.
psd=n makes a name the organizational domain for itself and everything below
Section 4.7 defines psd=n as a record published for a domain that is not a PSD but that is the organizational domain for itself and its subdomains. It does two things at once. It stops the walk at that name (Section 4.10, steps 2 and 6), and it wins the organizational domain selection outright as rule 1 of Section 4.10.2, ahead of any psd=y above it and ahead of the fewest-labels rule.
Section 5.1.8 gives the intended use: in an organization that delegates subtrees of its namespace to local departments, psd=n lets a department declare an organizational domain of its own, typically because it wants different policy settings or different reporting URIs than the apex record. Section 11.8 gives a defensive use: if a PSD above your domain publishes a DMARC record without the appropriate psd=y tag, a receiver can conclude that the PSD is your organizational domain, which widens relaxed alignment beyond your administrative boundary; adding psd=n to your own record removes that possibility. Section 11.8 also recommends checking the DMARC records of PSDs above you periodically, since you have no control over when they change.
psd=n cuts the subtree loose in both directions. Relaxed alignment is defined in Section 3.2.10.1 as the Author Domain and an authenticated identifier having the same organizational domain. Once corp.example.com carries psd=n, names under it have corp.example.com as their organizational domain while names under the apex still have example.com, so a DKIM signature with d=mail.example.com no longer relaxes into alignment with an Author Domain of mail.corp.example.com. Add the tag when you want that separation, not to save DNS queries.
What a record on a subdomain does, and does not, end
It is worth being exact here, because the shorthand that "a record on a subdomain ends the walk" is only true in specific cases.
At the Author Domain itself, discovery ends before the walk begins. Section 4.10.1 starts with a query for _dmarc prepended to the Author Domain, and if a valid record is found there, that is the record applied. Its p tag supplies the policy. This is the one placement that guarantees a specific record governs a specific sending name, and Section 5.1.8 turns it into a MUST for Author Domains longer than eight labels.
At an intermediate name, only psd ends the walk. Steps 2 and 6 of Section 4.10 stop only on psd=n or psd=y. A record with no psd tag is kept as a candidate, and the walk carries on above it.
An intermediate record is consulted only if it wins the selection. Under rule 3 of Section 4.10.2 that means being the record found at the name with the fewest labels. Appendix B.4.1 works through exactly this: records exist at both signing.example.com and example.com, and example.com is the organizational domain because it is the highest element in the tree with a record.
So the inheritance picture is not "the nearest record above you wins". It is: your own name first, then whichever record the walk finds highest, unless somebody along the way used psd to say otherwise. Finding that a policy you published is never applied usually means the record sits at a name that is neither the Author Domain nor the organizational domain.
Measuring it: the discovery_method field in aggregate reports
You do not have to guess which method a receiver ran. RFC 9990 Section 3.1.1.5 adds an optional element named discovery_method to the policy_published block of the aggregate report, describing the method used to discover the DMARC policy record used during evaluation. It has exactly two permitted values, fixed by the DiscoveryType simple type in the XML schema in Appendix A of RFC 9990: psl for the method from RFC 7489, and treewalk for the method in RFC 9989.
Read it together with the domain element in the same block, which RFC 9990 Section 3.1.1.5 defines as the DMARC Policy Domain, that is, the name at which the applicable record was discovered (RFC 9989 Section 3.2.5). The pair tells you two things a policy record alone cannot: which of your records that receiver actually applied, and how it got there. If you publish at several levels, this is the field that shows whether receivers are landing where you intended.
The element is optional. Its absence means the receiver did not state a method, not that it used the PSL. Aggregate reports from receivers that still run the RFC 7489 method are equally valid reports, and policy_published/domain remains the reliable signal in that case, because it names the record that was actually used regardless of how it was found.
Which receivers follow which version — and what that means for your record
Since May 2026 there is exactly one valid DMARC specification: RFC 9989, together with
its two companion documents on aggregate reporting (RFC 9990) and failure reporting
(RFC 9991). RFC 7489 is obsolete. The RFC Editor states it on the document’s own information page in exactly those words: “This RFC is now obsolete, see RFC 9989, RFC 9990, RFC 9991.”
Obsolete is a statement about the document, not about the software in the field. Every
record you publish today is read by two populations of receivers at once: those that
have implemented RFC 9989, and those still running an RFC 7489 implementation. Neither
population announces itself. A record that is correct under both is therefore not a
compromise — it is the only kind of record whose behaviour you can predict.
What actually changed in May 2026
One detail is easy to miss, and it changes how you should read the transition: this was
never standard against standard. RFC 7489 was not the product of an IETF work
stream. It was published through the Independent Submissions Editor and classified as
Informational. RFC 9989 states this plainly in Appendix C.1 and contrasts itself as
“an Internet Standards Track document”. This is the first DMARC document on the
standards track: RFC 7489 was Informational, and RFC 9091, which introduced PSD DMARC,
was Experimental (Appendix C.3). For the eleven years between RFC 7489 and RFC 9989,
DMARC was deployed at scale without a standards-track specification behind it.
RFC 9989 obsoletes RFC 7489 and RFC 9091 (the experimental PSD DMARC document). The
practical consequences for a published record sit in four places.
What your record says
An RFC 7489 receiver
An RFC 9989 receiver
pct=25
Applies the policy to a sample of failing messages
Ignores the tag entirely; the full policy applies to every failing message
Nothing — the Organizational Domain is derived
Derived from a Public Suffix List
Derived from the DNS tree walk (§4.10), which can give a different answer
np=reject
Unknown tag, ignored; non-existent subdomains fall back to sp or p
Applies reject to non-existent subdomains (§4.7)
psd=y
Unknown tag, ignored
Marks the name as a Public Suffix Domain and steers the tree walk (§4.10.2)
t=y
Unknown tag, ignored; the full policy applies
Applies the policy one level weaker (§4.7); the exemption is recorded in the report as policy_test_mode (RFC 9990 §3.1.6)
None of the tags in the right-hand column break an older receiver's parsing. Both
specifications require unknown tags to be ignored, and RFC 9989 §9.3 states the
design rule directly: tags added to DMARC “are to avoid changing the semantics of
existing records when processed by implementations conforming to prior
specifications”. Adding np, psd or t is safe.
Relying on them for an outcome you need today is not.
1. pct — read by one population, invisible to the other
RFC 9989 removed pct (Appendix C.5.2). The IANA DMARC Tags registry
(§9.3) now lists it with status historic, alongside rf and
ri. Appendix A.6 gives the reason, and it is worth reading before you trust
any percentage you have deployed: operational experience showed the tag
“was usually not accurately applied, unless the value specified was either 0 or 100”,
and the inaccuracies at other values “varied widely from one implementation to
another”.
So a record carrying pct=25 today does not mean a quarter of failing mail is
quarantined. It means an unknown share is, at the receivers that still honour the tag,
and all of it is at every RFC 9989 receiver. The gradual ramp of
pct=10 → 25 → 50 is not partially
effective any more; on the newer population it is inert. See
the rollout section for what replaces it.
Leaving the tag in place does no damage: under RFC 9989 §4.8 unknown tags are
ignored and stray syntax is discarded rather than invalidating the record. The risk is
not breakage, it is a false sense of what your policy currently does.
2. Organizational Domain — Public Suffix List against tree walk
This is the divergence with the widest blast radius, because the Organizational Domain
decides two separate things: which record applies to a subdomain that has none of its
own, and whether a DKIM or SPF identifier counts as relaxed-aligned with the
From: domain.
RFC 7489 derived it from a Public Suffix List. RFC 9989 §4.10 replaced that with the
DNS tree walk: query _dmarc at successively shorter names, at most eight
queries, and pick the Organizational Domain from the records actually found
(§4.10.2). The specification names the consequence itself in Appendix C.3:
“These algorithm changes introduce the possibility of interoperability issues where a
Domain Owner expects a DMARC Policy Record or an Organizational Domain to be identified
by the Tree Walk process, but a Mail Receiver using an implementation of DMARC based on
[RFC7489] and relying on a PSL might arrive at a different answer”.
Two situations produce different answers in practice. The first is a record published at
an intermediate level: under §4.10.2 a record carrying psd=n makes that
name the Organizational Domain, which a list-based receiver has no way of seeing. The
second is less obvious.
A list-based receiver reduces both news.example.com and
bulk.example.com to the Organizational Domain
example.com, finds them relaxed-aligned, and the message passes.
A tree-walk receiver runs §4.10.2 twice. Starting at
news.example.com it retrieves one record, at that same name, so that name
becomes the Organizational Domain. Starting at bulk.example.com it
retrieves no record at all, and the rule for that case is explicit: the initial target
domain is the Organizational Domain. The two Organizational Domains differ, the
signature is not aligned, the message fails, and p=reject applies to it.
The same message passes on one population and is rejected on the other — and the
newer population is the one that rejects it.
The fix is the one RFC 9989 §5.1.4 asks for anyway: publish a record for the
Organizational Domain as well, not only for the Author Domain you send from. With a
record at _dmarc.example.com, both tree walks end at
example.com and the two populations agree again. Appendix C.3 names the
same remedy: the issue “is entirely avoided by the use of strict alignment and
publishing explicit DMARC Policy Records for all Author Domains used in an
organization's email”.
3. np and psd — tags the older population never learned
np sets the policy for non-existent subdomains of the Organizational Domain
(§4.7); non-existent has the precise meaning of §3.2.13, an NXDOMAIN response.
psd marks a name as a Public Suffix Domain, and RFC 9989 §5.2 requires
a Public Suffix Operator publishing a record to include psd=y.
An RFC 7489 receiver ignores both. That matters most for np, because the
fallback is spelled out in §4.7: when np is absent, the policy from
sp applies if present, otherwise the policy from p. So
p=none; np=reject gives you protection for non-existent subdomains on one
population and nothing at all on the other. If the protection matters to you, it has to
come from p or sp as well, not from np alone.
The rule that follows from all three. Write a record that is correct
under both specifications, and never depend on a single tag for an outcome you need.
Concretely: publish an explicit record for every Author Domain you actually send from
and for the Organizational Domain above them, so no receiver has to guess where
your namespace begins; carry the policy in p and sp, which both
populations read, and treat np and psd as additions on top;
and read any pct value below 100 as expressing an intent that only part of
the field still honours.
The three policy levels: none, quarantine, reject
The p tag carries what RFC 9989 calls the Domain Owner Assessment Policy:
your message handling preference for mail that uses your domain in the
From: header and does not pass DMARC validation. §4.7 defines exactly
three values, and the wording is worth taking literally, because each one says something
different about how certain you are.
p=none — “The Domain Owner offers no expression of preference”
That is the entire definition in §4.7. It is not a weak policy; it is the absence of
one. RFC 9989 backs this up on the receiver side in §5.4: discovered policies of
p=none “MUST NOT modify existing mail handling processes”. A receiver
that changes anything about how it treats your failing mail because you published
p=none is not conforming.
What p=none does buy you is data. §3.2.12 defines this state as
Monitoring Mode — the policy is none throughout the domain and its
subdomains, and the owner is receiving aggregate reports and using them to correct its
authentication. Without a rua tag, p=none is a measurement
phase that measures nothing.
p=quarantine — “considers such mail to be suspicious”
The full sentence in §4.7 is: “The Domain Owner considers such mail to be
suspicious. It is possible the mail is valid, although the failure creates a significant
concern.” The admission that the mail might be valid is the point of the level. You
are asking for the message to be treated with suspicion, not destroyed, because you are
not yet certain that everything legitimate authenticates.
What suspicion means in practice is not defined by the RFC and cannot be. §5.4 opens
with the governing sentence for all three levels: “The final handling of any message is
always a matter of local policy and is left to the discretion of the Mail Receiver.”
One receiver files the message in a junk folder, another adds a warning, another defers.
p=reject — “a clear indication that the use of the domain name is not valid”
§4.7: “The Domain Owner considers all such failures to be a clear indication that
the use of the domain name is not valid.” Note the scope — all such
failures. Publishing p=reject is a statement that you know of no legitimate
mail stream that fails DMARC for your domain. If one exists, you have asserted that it is
invalid.
§7.2 describes how receivers carry that out and why the choice is not neutral. A
full rejection is a 5xy reply to the DATA command, leaving the sending client to generate
the failure notice. A silent discard returns a 2xy reply and drops the message, which
avoids backscatter but means the server “has to be programmed to give a false result,
which can confound external debugging efforts”. The same section suggests putting the
word DMARC into the reply text so the reason is machine-detectable, for example
550 5.7.1 Email rejected per DMARC policy for example.com.
Treats the message as suspicious, in whatever way local policy defines
May reject in the SMTP session (§7.2), but is required to weigh other evidence first (§7.4)
What you risk
Nothing operationally, and no protection against anyone using your domain
Legitimate mail that fails alignment lands in a junk folder rather than the inbox
Legitimate mail is lost, and indirect flows such as mailing lists and forwarders break (§7.4)
What the report shows
policy_published p=none, disposition none, with the real alignment results still recorded
Disposition quarantine, or none with a reason element if the receiver overrode you
Disposition reject, or a weaker disposition with an override reason such as mailing_list or trusted_forwarder
Report structure per RFC 9990: policy_published carries the record that was
discovered (§3.1.1.5), policy_evaluated carries the disposition actually
applied plus the DMARC alignment results for DKIM and SPF (§3.1.1.9), and the
reason element carries a pre-defined override type from the list in
§3.1.6. RFC 9990 §3.1.1.9 is explicit that the reported dkim and
spf values “MUST be the evaluated values as they relate to DMARC, not the
values the receiver may have used when overriding the policy”. Your alignment picture
stays honest even where the disposition does not follow your policy.
Why p=reject is not a guarantee
This is the most consequential thing RFC 9989 says about policy, and it is the part
most often skipped: a conformant receiver is not permitted to reject on your say-so
alone.
What p=reject does
Tells receivers you consider every DMARC failure on your domain invalid (§4.7).
Puts the domain into Enforcement as §3.2.9 defines it, provided no subdomain policy weakens it.
Makes unauthenticated use of your domain a high-value negative signal to receivers that honour it (§3.2.9).
Requires you, in return, to sign your outbound mail with DKIM (§7.4, §8).
What p=reject does not do
Compel rejection. §7.4: receivers “MUST NOT reject incoming messages solely on the basis of a ‘p=reject’ policy”, and §8 repeats it as a conformance requirement.
Guarantee even quarantine-level handling of every failure — §5.4 allows a receiver to accept failing mail regardless of your policy.
Reach mail that never gets that far: §7.1 notes an SPF -all hard fail can cause rejection before DMARC runs, and such messages never appear in aggregate reports at all.
Stop display-name spoofing. §11.4 states it directly: such attacks “are out of scope for DMARC”, because DMARC validates the domain of the From: address and not the human-readable name in front of it. Lookalike domains are outside its scope for the same reason.
The floor RFC 9989 sets is deliberate: “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’” (§7.4). A receiver with no further evidence is
required to downgrade you by one level. And §5.4 covers the opposite case too: a
DMARC pass “does not carry an explicit or implicit value assertion about that message or
the Domain Owner”, so receivers may still reject or quarantine mail that passes.
The two ways you end up on p=none without asking
§4.7 and §4.10.1 both describe records that are treated as p=none
regardless of intent:
A syntactically valid record with no p tag “is treated as if it included
‘p=none’” (§4.7).
A record whose p tag is invalid, or whose sp or
np tag is invalid, is salvaged only if it carries at least one
syntactically valid rua URI. In that case the receiver “MUST act as if a
record containing ‘p=none’ was retrieved”. Without a usable
rua, the receiver applies no DMARC processing to the message at all
(§4.10.1).
A typo in p=rejcet therefore does not fall back to your previous policy and
does not raise an error anywhere you would see it. It silently becomes monitoring, or
silently switches DMARC off for your domain.
Rollout: from p=none to p=reject
RFC 9989 sets out the sequence in §5.1: publish SPF for an aligned domain
(§5.1.1), sign with an aligned DKIM domain (§5.1.2), set up a reporting mailbox
(§5.1.3), publish the record (§5.1.4), read the reports (§5.1.5),
remediate what they show (§5.1.6), and only then decide about Enforcement
(§5.1.7). The order is not decorative: §5.1.6 states that authentication
shortcomings in legitimate mail streams “MUST be addressed prior to any attempt by the
Domain Owner to publish a Domain Owner Assessment Policy ... of Enforcement”.
The phases
Phase
Record
Minimum duration
When to move on
When to stop
Monitoring
v=DMARC1; p=none; rua=mailto:dmarc@example.com
One month, per §7.4
Every legitimate stream in the reports produces an aligned pass, and you recognise every sending source
Reports still show legitimate senders you cannot identify or cannot fix
Never entirely: the indirect-flow problems in §7.4 do not go away, so keep reading the reports
Two points about the duration column. First, the figures are a floor, not a
recommendation. §7.4 states them for domains that host users who post to mailing
lists and intend to publish p=reject anyway: such a domain
“SHOULD first take advantage of DMARC aggregate report data for their domain to
determine the possible impact to their users, first by publishing ‘p=none’ for
at least a month, followed by publishing ‘p=quarantine’ for an equally long
period of time, and comparing the message disposition results”. It is the only
duration RFC 9989 names.
Second, §5.1.7 makes clear that a month is often nowhere near enough:
“Depending on its cadence for sending mail, it may take many months of consuming DMARC
aggregate reports before a Domain Owner reaches the point where it is sure that it is
properly authenticating all of its mail”. Annual mail — a renewal notice, a tax
document, a yearly statement — will not appear in a month of reports at all. The
duration that matters is the one that covers a full cycle of your own sending, not a
number on a calendar.
Note also that Enforcement in the sense of §3.2.9 requires the policy to be other
than none for the Organizational Domain and all subdomains below it.
A record with p=reject; sp=none is not Enforcement, and it leaves every
subdomain of your namespace usable by anyone.
The t=y ladder replaces the pct ramp
The familiar pct=10 → 25 → 50 ramp no
longer works, for the reasons in the pct section above. RFC 9989
replaces it with t=y, and Appendix A.6 explains the reasoning: the two
usable values of pct were 0 and 100, so the tag was replaced by one with
exactly two values, “meant to be analogous in their application by mailbox providers
and intermediaries to the ‘pct’ tag values ‘0’ and ‘100’,
respectively”.
What t=y does is defined in §4.7: it asks the validating receiver not to
apply the policy, but to apply any special handling rules it has, with the expectation
that the policy applied to failing messages “will be one level below the specified
policy”. p=quarantine; t=y is handled as none;
p=reject; t=y is handled as quarantine. The tag has no effect
on a policy of none, which is why there is no p=none; t=y rung.
The reason this works as a rollout instrument, and a percentage never did, is that the
reporting does not weaken with the policy. §4.7 states that the tag
“does not affect the generation of DMARC reports”. Better still, the weakening is
visible in the data: RFC 9990 §3.1.6 defines the override type
policy_test_mode as follows: “The message was exempted from application of
policy by the testing mode (‘t’ tag) in the DMARC Policy Record”. The same document
puts the value of the t tag into the policy_published element
as testing (§3.1.1.5). So a test-mode rung gives
you a labelled count of exactly the mail the next rung would have hit — which is more
than a sampling ramp ever told you, because you can attribute every exempted message.
If you need the same behaviour on RFC 7489 receivers during the transition, the
corresponding value there is pct=0, and publishing both is safe: an RFC 9989
receiver ignores pct without invalidating the record (§4.8). Treat that
pairing as temporary, and read Appendix A.6 first — it records that pct=0
took on its meaning as an unintended side effect, that implementations acted on it
differently, and that the behaviour it triggered was usually From-header rewriting rather
than a policy step-down. The two are analogous, not identical.
Two conditions the RFC attaches to p=reject
RFC 9989 does not treat p=reject as the destination for every domain. It
attaches two requirements, both in §7.4, and repeats the first in the conformance
list in §8.
You may not rely on SPF alone. §7.4 explains why: when mail is
relayed by an alumni forwarder, a role address or a mailing list, the connecting IP is
the relay's, so “it will most likely fail SPF checks unless the RFC5321.MailFrom
address is rewritten by the relaying MTA”, while “DKIM signatures will generally
remain valid in these relay situations”. The requirement is stated as an emphasised
rule: “It is therefore critical that domains that publish ‘p=reject’ MUST NOT
rely solely on SPF to secure a DMARC pass and MUST apply valid DKIM signatures to their
messages.” §8 lists the same MUST NOT among the conformance requirements for
Domain Owners.
If your users post to public mailing lists, weigh p=reject carefully.
§7.4: “It is therefore critical that domains that host users who might post
messages to mailing lists SHOULD NOT publish Domain Owner Assessment Policies of
‘p=reject’.” Appendix C.6 records this as a deliberate change from RFC 7489
— the new document “makes explicit that domains for general-purpose email SHOULD NOT
deploy a DMARC policy of ‘p=reject’”. The failure mode named in §7.4 is
not only a bounced message: when a list receives rejections for a subscriber, the list
software reads the address as dead and unsubscribes it.
§7.4 is also candid about where this has landed in practice. Mailing list software
has adopted From-header rewriting as a fact of life; ARC (RFC 8617) and other attempts to
preserve the original header have not achieved wide use; and “few Mail Receivers apply
any mitigation techniques when receiving indirect mail flows”. If your domain sends
only transactional and corporate mail, the mailing-list problem does not apply to you and
p=reject is the right destination — though note that §7.4's own worked
example is a bank, whose targeted mail still reaches recipients through alumni forwarders
and role addresses. Forwarding is not a mailing-list-only phenomenon, which is exactly
why the DKIM requirement above is not optional. If your domain hosts general-purpose
mailboxes, the RFC's advice is to establish the impact from aggregate reports before you
get there, which is what the phases above are for.
Example records for real situations
Every record below is shown complete. Nothing is abbreviated for the page, so what you read is what you would publish, and the DNS name in front of each record is part of the example: a DMARC record is published under the name it applies to, prefixed with the _dmarc label, and a record published at an Organizational Domain can also apply to subdomains of that name (RFC 9989 §4.5).
None of these records contains pct, ri, or rf. RFC 9989 removed all three tags (Appendix C.5.2), and the IANA DMARC Tags registry now lists them as historic (§9.3). Where a record would previously have carried pct to phase a stricter policy in gradually, it carries t=y instead: RFC 9989 Appendix A.6 introduces t explicitly as the replacement for the only two pct values that were applied consistently in practice, pct=0 and pct=100.
1. First step: monitoring, no enforcement
_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com"
This is the record you publish before you know where your mail comes from, and RFC 9989 §5.1.4 names it as the usual starting point: p=none plus a rua address pointing at a mailbox you have already set up to receive aggregate reports. The reason for starting here is that in all but the most trivial setups a domain owner overlooks a server or is unaware of a third-party sending arrangement, and the aggregate reports are how you find those (§5.1.5).
Leaving out the rua tag turns this record into a complete no-op: with no rua present, mail receivers MUST NOT generate aggregate reports for the domain (§4.7), so you get neither protection nor the data the record exists to collect.
2. Test phase before tightening
_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; t=y; rua=mailto:dmarc-reports@example.com"
The t tag is a request that the validating actor not apply the declared policy but instead apply any special handling rules it has, on the expectation that failing messages are treated one level below the stated policy (§4.7). With p=quarantine; t=y that means failing mail is handled as if the policy were none, while your reports still name the policy you published, carry the t value in the policy_published element and record the exemption as a policy_test_mode override reason (RFC 9990 §3.1.1.5, §3.1.6); RFC 9989 Appendix B.2.5 uses exactly this construction for a subdomain being brought into service.
Adding t=y to a record whose policy is none changes nothing at all, because the tag has no effect on a policy of none (§4.7) — and note that t never affects report generation, so it cannot be used to turn reporting on or off.
3. End state for a sending domain
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=reject; np=reject; adkim=s; aspf=s; rua=mailto:dmarc-reports@example.com"
This is the strictest combination of policy and alignment the tags allow, and the sp and np tags here are deliberate restatements rather than additions: p already applies to the domain and to its subdomains unless sp or np overrides it (§4.7), so spelling both out mainly guards against a later edit of p silently loosening everything below the domain. Before publishing it, be aware that a domain at p=reject MUST NOT rely on SPF alone for its DMARC pass and MUST apply valid DKIM signatures to its messages, because forwarding usually preserves the DKIM signature and usually breaks SPF (§7.4).
The strict alignment tags are what will break your mail: under adkim=s and aspf=s the authenticated identifier must be identical to the author domain, so a message from example.com signed by mail.example.com, which aligns under the relaxed default, stops aligning the moment you publish this record (§4.4).
4. One subdomain that is not ready yet
_dmarc.newsletter.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com"
Policy discovery prefers a record published at the author domain itself over the record at the organizational domain (§4.10.1), so this record on the subdomain overrides an enforcing record at example.com for mail whose From header uses newsletter.example.com. That lets the rest of the organization stay at p=reject while one lagging mail stream is still being fixed, with its own reports separated from everything else.
Do not put an sp tag in a record like this one: sp is ignored in DMARC records published on subdomains of organizational domains, because policy discovery stops at the author domain and never treats this record as the subdomain policy of anything (§4.7).
5. Parked domain that never sends mail
_dmarc.example.net. IN TXT "v=DMARC1; p=reject; sp=reject; np=reject; rua=mailto:dmarc-reports@example.com"
A domain you own but never send from is the one case where you can publish p=reject immediately, since there is no legitimate mail stream to break and no interoperability cost of the kind §7.4 describes. Spelling out sp and np covers both halves of the namespace below it: sp governs existing subdomains, np governs non-existent ones (§4.7).
The DMARC record states a handling preference; it does not stop anyone from attempting delivery to the domain. Two records complete the picture and are independent of DMARC: a Null MX record (MX 0 .) declaring that the domain accepts no mail, and an SPF record ending in -all authorising no sending host at all. See the Null MX generator for the exact syntax.
The np tag covers less than it sounds like it does, because DMARC treats a name as existing if any resource record of any type exists for it (RFC 9989 Appendix A.4) — a leftover A record on www.example.net makes that name exist, so it falls under sp and not under np.
6. Google Workspace as the only sending path
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=reject; np=reject; rua=mailto:dmarc-reports@example.com"
Nothing in a DMARC record names a mail provider, and there is no tag in which a provider could be named (§4.7), so the record for a domain hosted at Google Workspace is the ordinary enforcing record. What has to be true before you publish it lives entirely outside this record: the domain's SPF record must authorise the provider's sending hosts so that the RFC5321.MailFrom domain produces an aligned SPF-authenticated identifier (§5.1.1), and the provider must sign with a DKIM key published in your DNS so that the signing domain in the d= tag aligns with the author domain (§5.1.2).
Watch the paths that leave the provider's normal outbound route — a forwarding or relay arrangement in which the MAIL FROM domain stays inside the provider's own domain still earns an SPF pass, but the authenticated identifier is then the provider's domain and does not align with yours (§4.4.2), which is precisely the kind of stream aggregate reports are there to surface before you enforce.
7. Microsoft 365 as the only sending path
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=reject; np=reject; rua=mailto:dmarc-reports@example.com"
This record is character-for-character the record in the previous case, and that is the honest answer rather than an omission: the DMARC record is provider-neutral, so there is no Microsoft 365 variant of it and no Google Workspace variant of it. The difference between the two setups is confined to the records DMARC depends on — which SPF mechanism authorises the provider's outbound hosts, and which DKIM selectors under your domain carry the provider's keys — and those two requirements are stated generically in §5.1.1 and §5.1.2 because DMARC does not care which vendor satisfies them.
With a single provider it is tempting to enforce once SPF passes and leave DKIM for later, but a domain publishing p=reject MUST NOT rely solely on SPF to secure a DMARC pass and MUST apply valid DKIM signatures to its messages (§7.4).
8. Newsletter through an ESP with its own bounce domain
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=reject; np=reject; rua=mailto:dmarc-reports@example.com"
This is the classic alignment case, and the record carries no alignment tags on purpose: with the default relaxed mode, a return-path subdomain such as bounce.example.com operated by the provider produces an SPF-authenticated identifier that shares the organizational domain example.com with the author domain, which is exactly what relaxed alignment requires (§4.4). If the provider instead sends with a bounce domain inside its own namespace, SPF still passes, but it passes for the provider's domain, and DMARC only accepts an SPF-authenticated identifier that aligns with the author domain (§4.4.2).
Handing a subdomain to an external party has a cost worth knowing: whoever controls the SPF record for a subdomain of your organizational domain can, under relaxed alignment, produce mail that passes DMARC for the organizational domain itself (§11.8). Keep that delegation as narrow as the arrangement requires.
Adding aspf=s to this record breaks the arrangement it is meant to support, because strict alignment demands that the authenticated identifier be identical to the author domain, and bounce.example.com is not identical to example.com (§4.4).
9. Transactional mail through a second service
_dmarc.tx.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc-tx@example.com"
When a second service sends receipts and password resets alongside your mailbox provider, giving that stream its own author domain and its own record separates its reports from your ordinary correspondence and lets you tighten or loosen it independently of example.com. Discovery finds this record first for mail whose From header uses tx.example.com, and the organizational domain record continues to apply to every author domain that has no record of its own (§4.10.1).
The separation only works if the service actually puts tx.example.com in the From header, because the author domain from which the policy is discovered is the domain extracted from the RFC5322.From header field (§3.2.2, §4.10.1) — a configuration that uses tx.example.com only as the return path while the From header stays at example.com never consults this record at all.
10. Reports to an outside analysis provider
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=reject; np=reject; rua=mailto:dmarc@example.com,mailto:example-com@reports.example.net"
The rua tag takes a comma-separated list, so a domain can send its aggregate reports to its own mailbox and to a report processor at the same time. The first address is inside the publishing domain and needs nothing further; the second is not, and a receiver that finds a report address outside the organizational domain of the record MUST verify that the destination has agreed to the arrangement (RFC 9990 §4).
The record that grants that permission is published by the analysis provider in its own zone, not by you — for this example it is a TXT record at example.com._report._dmarc.reports.example.net containing at least v=DMARC1, and a provider willing to accept reports for any domain may publish it as a wildcard instead. The construction of that name and the override rule that comes with it are set out under the authorisation record.
If that authorisation record is missing, unparseable, or does not begin with v=DMARC1, the external URI MUST be ignored by the receiver generating the report (RFC 9990 §4), and nothing anywhere tells you so: the reports to your own mailbox keep arriving while the provider's dashboard stays empty.
RFC 9989 §11.6 recommends, where it is possible, sending reports to an address that can receive mail inside your own domain and forwarding them onward from there, which avoids the external authorisation step entirely. For a domain that cannot receive mail at all, such as the parked domain above, the authorisation record is the only route.
Reading aggregate reports (RFC 9990)
An aggregate report is an XML document that a Mail Receiver sends to the addresses in
your rua tag. It contains counted message groups, not messages: for every
sending IP address and every combination of authentication results, one row with a
message count, the DMARC disposition that was applied, and the raw SPF and DKIM results
behind it. RFC 9990 Section 7.2 is explicit that this data carries no personal
information — no individual mail addresses, no end-user IP addresses, no message
content. That is the trade: aggregate reports tell you the shape of your mail streams
and nothing about single messages.
Section numbers in this part of the page refer to RFC 9990 unless another document is
named.
One report covers exactly one DMARC Policy Domain and one policy configuration
(Section 3.1). If your subdomains have no DMARC record of their own, their traffic
appears as additional record elements inside the organizational domain's
report; a subdomain that publishes its own record gets its own report (Section 3.1.2).
If you change your policy mid-period, a receiver may send you a single report with a
mix of dispositions under one policy_published element, or several reports
for the same period — both are allowed by Section 3.3, so a brief inconsistency after a
policy change is expected rather than a bug.
Period and frequency
The date_range element carries begin and end as
seconds since the epoch, in UTC. Section 3.1.1.4 states that these denote the reporting
period rather than the first and last observed message, that periods SHOULD NOT
overlap, and that the period will typically encompass a single UTC day beginning at
0000 UTC. Section 3.1 describes the cadence as daily or more frequent, and RFC 9989
Section 8 lists "SHOULD send aggregate reports on at least a daily basis" among the
conformance requirements for Mail Receivers.
You can no longer ask for a different interval. The ri tag, which requested
a reporting interval in the old specification, is listed under "Tags Removed" in
RFC 9989 Appendix C.5.2 and carries the status "historic" in the DMARC Tags registry
(RFC 9989 Section 9.3). Publishing ri=3600 today is inert: it is an unknown
tag, not an hourly report.
How the reports reach you
By email. RFC 9989 Section 8 requires a conforming Mail Receiver to support the
mailto: URI for sending requested reports, and RFC 9990 Section 3.5.2
defines the mechanics: the report is an XML file attached to a MIME message, SHOULD be
GZIP compressed, and MUST carry the media type application/gzip when
compressed and text/xml when not. The filename follows a fixed ABNF —
receiver!policy-domain!begin-timestamp!end-timestamp[!unique-id].xml.gz —
and the Subject field follows one too, so both are parseable without opening the
attachment.
Two consequences worth knowing. First, the mail stream carrying the reports MUST itself
produce an aligned DMARC pass (Section 3.5.2); a report that arrives unauthenticated is
one you should not trust. Second, a receiver evaluates the URIs in your rua
tag in the order given, ignores malformed ones, and attempts delivery to every remaining
URI up to its own limit on supported URIs (Section 3.5). Where the URI does not say
otherwise, it SHOULD use a channel employing TLS (SMTP with STARTTLS).
What about https:? RFC 9989 Section 4.7 allows any valid
URI in rua, but it also states that URIs involving schemes not supported
by a Mail Receiver MUST be ignored, and only mailto: must be implemented.
RFC 9990 Section 3.5.3 covers other methods in a single sentence: the specification
allows for other registered URI schemes to be supported in later versions. There is no
HTTPS submission mechanism defined in RFC 9990. An https: URI in your
record is syntactically acceptable and operationally silent.
A complete report, annotated
The namespace is urn:ietf:params:xml:ns:dmarc-2.0, registered by IANA in
RFC 9990 Section 6.1. The document below is one UTC day for example.com with two
records: mail from the domain's own platform, and mail that reached the receiver through
a mailing list.
<feedback xmlns="urn:ietf:params:xml:ns:dmarc-2.0">
<version>1.0</version>
<report_metadata>
<org_name>Example Receiver</org_name>
<email>dmarc-reports@receiver.example.net</email>
<extra_contact_info>https://receiver.example.net/postmaster</extra_contact_info>
<!-- Unique among reports to this domain; used to spot duplicates. -->
<report_id>1735689600-example.com@receiver.example.net</report_id>
<date_range>
<begin>1735689600</begin> <!-- 2025-01-01 00:00:00 UTC -->
<end>1735775999</end> <!-- 2025-01-01 23:59:59 UTC -->
</date_range>
<!-- An optional <error> element would sit here, carrying problems
the receiver hit while reading your DMARC record itself. -->
<generator>Example Aggregate Reporter v2.1</generator>
</report_metadata>
<policy_published>
<domain>example.com</domain>
<discovery_method>treewalk</discovery_method> <!-- or "psl" -->
<p>quarantine</p>
<sp>quarantine</sp>
<np>reject</np>
<fo>1</fo>
<adkim>r</adkim>
<aspf>r</aspf>
<testing>n</testing> <!-- value of the "t" tag -->
</policy_published>
<!-- Record 1: the domain's own sending platform. Everything aligned. -->
<record>
<row>
<source_ip>192.0.2.24</source_ip>
<count>4213</count>
<policy_evaluated>
<!-- "pass" means: DMARC passed under an enforcing policy. -->
<disposition>pass</disposition>
<dkim>pass</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
<envelope_from>bounces.example.com</envelope_from>
</identifiers>
<auth_results>
<dkim>
<domain>example.com</domain>
<selector>mail202601</selector> <!-- REQUIRED since RFC 9990 -->
<result>pass</result>
</dkim>
<spf>
<domain>bounces.example.com</domain>
<scope>mfrom</scope> <!-- the only valid value -->
<result>pass</result>
</spf>
</auth_results>
</record>
<!-- Record 2: your mail relayed by a discussion list on example.org.
Read the two blocks against each other before drawing conclusions. -->
<record>
<row>
<source_ip>2001:db8::a12</source_ip>
<count>17</count>
<policy_evaluated>
<!-- Published policy is quarantine, applied disposition is none,
so a reason element is mandatory here. -->
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
<reason>
<type>mailing_list</type>
<comment>Message arrived via a known list; failure expected.</comment>
</reason>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
<!-- The list rewrote the envelope sender to its own domain. -->
<envelope_from>lists.example.org</envelope_from>
</identifiers>
<auth_results>
<!-- Passing signatures first, per the priority list in 3.1.3. -->
<dkim>
<domain>lists.example.org</domain>
<selector>list2026</selector>
<result>pass</result> <!-- valid, but not your domain -->
</dkim>
<dkim>
<domain>example.com</domain>
<selector>mail202601</selector>
<result>fail</result>
<human_result>body hash did not verify</human_result>
</dkim>
<spf>
<domain>lists.example.org</domain>
<scope>mfrom</scope>
<result>pass</result> <!-- SPF passed for the list, not for you -->
</spf>
</auth_results>
</record>
</feedback>
The second record is the one that trips people up. Inside auth_results,
SPF says pass and one DKIM signature says pass. Inside
policy_evaluated, both say fail. That is not a contradiction:
Section 3.1.1.11 defines auth_results as the raw SPF and DKIM results
"uninterpreted with respect to DMARC", while Section 3.1.1.9 requires the values in
policy_evaluated to be the DMARC-evaluated ones. The list's SPF pass belongs
to lists.example.org, which does not align with the From domain, and the signature that
would have aligned broke when the list modified the body. Whenever you compare the two
blocks, policy_evaluated is the verdict and auth_results is
the evidence.
Field
What it tells you
report_metadata/org_name, email
Which receiver produced the report and where to write if it looks wrong.
report_metadata/report_id
Unique among reports to the same domain, so duplicates are detectable (Section 3.5.1). Where a Report-ID also appears in the Subject field, it MUST be the same string (Section 3.1.4).
date_range/begin, end
Reporting period in seconds since the epoch, UTC, typically one day from 0000 UTC. Not the first and last message seen (Section 3.1.1.4).
report_metadata/error
Problems the receiver hit with your DMARC record itself: invalid rua, invalid sp, multiple DMARC records at one name (Section 3.1.5). Read this field first.
report_metadata/generator
Name and version of the reporting software, so you know where to report a malformed report (Section 3.1.1.3).
policy_published/domain
The DMARC Policy Domain this report is about — not necessarily the From domain of every record (Section 3.1.2).
policy_published/discovery_method
psl or treewalk: how this receiver found your record (Section 3.1.1.5).
policy_published/p, sp, np
The policy the receiver actually applied, including defaults it filled in. Compare against what you believe you published.
policy_published/testing
Whether the receiver saw t=y on your record (Section 3.1.1.5).
policy_published/adkim, aspf
The alignment modes in force. A record full of DKIM passes and DMARC fails usually means adkim=s against a subdomain signature.
row/source_ip
The connecting IP. Your only handle on who sent the mail; everything else in the record describes the message, not the sender.
row/count
Messages in this group. Counts cover all messages received, even those later blocked by other filtering (Section 3.1).
policy_evaluated/disposition
What happened: pass, none, quarantine or reject.
policy_evaluated/dkim, spf
The DMARC verdicts, i.e. authenticated and aligned. Only pass or fail occur here.
policy_evaluated/reason
Why the disposition differs from your published policy. Mandatory when alignment failed and the applied policy does not match your published policy (Section 3.1.1.9); type is one of five fixed values, comment is free text.
identifiers/header_from
The RFC5322.From domain — the identity DMARC protects.
identifiers/envelope_from
The RFC5321.MailFrom domain SPF was applied to. May be present but empty for a null reverse-path, e.g. bounces (Section 3.1.1.10).
identifiers/envelope_to
The recipient domain, when the receiver chooses to include it.
auth_results/dkim/domain, selector
The d= and s= of each evaluated signature. With the selector you can name the key, and therefore the sending platform, that failed.
auth_results/dkim/result
The DKIM result as such, from the set defined in RFC 8601: none, pass, fail, policy, neutral, temperror, permerror.
auth_results/*/human_result
Free-text detail on the failure, when the receiver provides it. The most useful field in the whole document, and the least often filled in.
auth_results/spf/domain, scope
The domain SPF checked and its source. mfrom is the only valid scope, because DMARC uses the MAIL FROM identity only (Section 3.1.1.13, RFC 9989 Section 4.4.2).
extension
Namespaced elements from a future extension, at file level or at the end of a record. A processor that does not understand one should skip it (Sections 3.1.1.6, 5).
Two structural details matter when you write a parser rather than read a report. The
first-level elements and the children of record, policy_evaluated
and auth_results MUST appear in the order the RFC lists them; the schema in
Appendix A models the remaining groups as xs:all, so the order of the children
of policy_published, row, identifiers and of a
single dkim block is not fixed. A report may carry up to 100 signatures
per row, ordered by the priority list in Section 3.1.3: strictly aligned passing
signatures first, non-passing ones last.
The new fields in RFC 9990 and what they reveal
RFC 9990 replaces the report format that was defined inside RFC 7489. Most changes are
small, and each of them answers a question you could not answer before. Appendix C of
RFC 9990 lists the differences; the table adds what each one is good for. Struck-through
rows are values that no longer exist — if you still see them, you are looking at a
report from an older generator.
Element or value
Defined in RFC 9990
What it reveals
discovery_method
3.1.1.5
Whether this receiver found your record with the PSL method (psl) or the DNS Tree Walk (treewalk) — the single best signal for how far the new specification has actually spread across your report senders.
testing
3.1.1.5
Whether the receiver saw your t=y. Together with the override type policy_test_mode it shows you exactly which traffic test mode is currently shielding from enforcement.
np
3.1.1.5
The policy in force for non-existent subdomains, echoed back. If it is missing from reports while it is present in your record, that receiver did not parse the tag.
generator
3.1.1.3
Name and version of the reporting software. When a report is malformed or a field is systematically empty, this tells you whose bug it is and where to report it.
extension and record-level extensions
3.1.1.6, 5
Two defined places for namespaced elements — one after policy_published, one at the end of a record. Extensions must carry a URI to their definition, and a processor that cannot handle one should ignore it and continue.
version
3.1.1.2
Now OPTIONAL, and when present it MUST have the value 1.0. A report without a version element is not defective; do not reject on that basis.
disposition value pass
Appendix A
The applied-disposition type gained a fourth value beside none, quarantine and reject. It means the message passed DMARC under an enforcing policy, so "no action taken" is no longer ambiguous between "passed" and "failed while p=none". The published policy tags p, sp and np keep the three-value set.
selector in a DKIM result
3.1.1.12, Appendix C
Now REQUIRED. Since one domain usually runs one selector per sending platform, a failing signature can finally be attributed to a specific key and provider instead of only to d=.
Override type policy_test_mode
3.1.6
New reason value: the message was exempted from policy by the t tag. It distinguishes "the receiver ignored my policy" from "my own test mode suspended it".
Override type sampled_out
—
Gone with the pct tag, which RFC 9989 Appendix C.5.2 lists as removed. Nothing is sampled out any more; partial deployment is expressed with t=y.
Override type forwarded
—
No longer in the list of five permitted values in Section 3.1.6. Forwarding cases now appear as trusted_forwarder, mailing_list, local_policy or other.
SPF scope value helo
—
The only valid scope is mfrom (Section 3.1.1.13). DMARC relies solely on SPF validation of the MAIL FROM identity (RFC 9989 Section 4.4.2), so a HELO-scoped SPF result never had a bearing on the DMARC verdict.
report_id structure
3.5.1
The identifier now has an ABNF of its own and MUST be unique among reports to the same domain, which makes duplicate detection mechanical rather than heuristic.
lang attribute on human-readable elements
3.1.1
extra_contact_info, error, comment and human_result take an optional lang attribute, default en. Worth handling if you feed those strings into a dashboard.
One policy domain per report
3.1, 3.1.2, Appendix C
Stated normatively: each report MUST contain data for one DMARC Policy Domain and one policy configuration. Reports from different receivers are now comparable without guessing how they grouped subdomains.
Common patterns and what they mean
Four situations account for most of what you will see in the first months of reading
reports. In each case, the row in policy_evaluated tells you what happened
and the block in auth_results tells you why.
An unfamiliar IP with SPF pass and DKIM fail. The
auth_results/spf domain aligns with your From domain, so the mail is coming
from something your SPF record authorizes, but no aligned signature exists — usually a
sending platform that a department enabled, got added to an include:, and
never had a DKIM key configured for. The second possibility is worse: your
include: chain covers a shared provider range, and another tenant on that
platform is passing SPF for your domain. Check whether the IP belongs to a provider you
have a contract with, then get that platform signing. RFC 9989 Section 8 states that a
Domain Owner MUST NOT rely solely on SPF for a DMARC pass when the policy is
p=reject, so an SPF-only stream is a blocker for enforcement, not a detail.
DKIM passes, SPF does not. This is the normal signature of forwarded
mail: the forwarder replaced the envelope sender with its own domain, so SPF was
evaluated for that domain and cannot align, while your signature survived the hop.
RFC 9989 Section 4.4.1 makes clear that any aligned DKIM-Authenticated Identifier is
enough for a DMARC pass, so these records are not a fault to fix. Confirm the shape
before you file it away: envelope_from should be someone else's domain, the
aligned DKIM result should be pass, and the disposition should be
pass. If the disposition is none with a
trusted_forwarder or mailing_list reason instead, the receiver
rescued the message rather than DMARC.
Both fail, at low volume. Small, scattered counts from IPs you do not
recognize, often on a subdomain you never send from, are the background noise of a
domain that exists: direct-domain forgery, backscatter and broken forwarding paths.
Check first whether the counts are stable over several days and whether the IPs cluster
in one network — a steady cluster is a forgotten sender, a wide scatter with one or two
messages each is abuse. Remember that count covers all messages the
receiver took in, including those its own filters later blocked (Section 3.1), so a
nonzero row is not evidence that anything was delivered. Do not delay enforcement over
this tail; it is exactly what p=reject is for.
Reports stop arriving. Work outwards from your own side. Does the
rua mailbox still accept mail, and does your filtering still let
application/gzip attachments through? Does the report stream still reach
you at all — reports MUST come from a DMARC-aligned mail stream (Section 3.5.2), so a
change on the reporter's side can make them bounce. Then check your record: a receiver
ignores malformed reporting URIs (Section 3.5), and if the record no longer contains a
valid p tag it is treated as p=none as long as one
syntactically valid rua URI remains (RFC 9989 Section 4.10.1) — reports can
therefore keep coming while your policy has quietly stopped applying, and stop when the
rua itself breaks. Finally, if your reports go to a third party, confirm
that the authorization record described in the next section still exists.
Reporting addresses do not have to be inside your own domain. That is deliberate — it
lets a domain with no mail infrastructure receive reports through a processor — and it
is also an attack: without a check, anyone could publish a record pointing
rua at a victim's address and then send a flood of failing mail so the
victim drowns in reports. RFC 9990 Section 4 defines the verification that prevents it.
The check runs when the Organizational Domain of the record differs from the
Organizational Domain of the URI's host. A rua pointing at another name
under your own Organizational Domain needs no extra record. If the domains differ, the
Mail Receiver builds a name from the URI host, the label sequence
_report._dmarc, and the domain the policy came from, and looks for a TXT
record there:
; DMARC record of the domain that wants reports
_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; "
"rua=mailto:dmarc@reports.example.net"
; Authorization published by the report processor
example.com._report._dmarc.reports.example.net. IN TXT "v=DMARC1"
; Or, to accept reports for any domain:
*._report._dmarc.reports.example.net. IN TXT "v=DMARC1"
The record is parsed as tag-value pairs in the same format as a DMARC Policy Record;
v=DMARC1 is mandatory and MUST appear first, a trailing semicolon is
optional, and anything that fails that test is discarded. One surviving record is enough
to authorize the arrangement. If the constructed name would exceed DNS length limits, no
positive determination can be made and the receiver stops — a real constraint for long
domain names, and the reason to keep the processor's host name short.
The authorization record may also contain its own rua tag, which overrides
the destination the Domain Owner requested. That override URI MUST use the same
destination host as the original, and if it names a different host again, the Mail
Receiver MUST NOT send a report to either URI. This is what lets a processor move
reports between its own mailboxes without every customer editing their record, while
keeping the arrangement from being used as a redirect. RFC 9991 Section 5 requires the
identical procedure for failure reports, with ruf substituted for
rua.
What the check gives you
Proof that the third party consented to receive reports about your domain, published in its own DNS.
A way for the processor to redirect reports to another mailbox on the same host, without your record changing.
An off switch on the processor's side: removing the record stops new reports, subject to the TTL, which RFC 9990 Section 4 calls out explicitly for a processor overcome by volume.
A wildcard form for processors that accept reports for arbitrary customer domains.
What it does not give you
Any guarantee that a report is genuine. Aggregate report data may be forged, and an attacker can submit false reports in volume to influence your decisions (RFC 9990 Section 8.2).
Control over the data once it arrives. A third party can perform traffic analysis on receiver metadata, which RFC 9990 Section 7.1 asks receivers to weigh before sending.
Any check at all when the destination is inside your own Organizational Domain — the procedure is not invoked there.
Delivery. If a destination is unreachable, the Mail Receiver MAY cache the data and retry, or MAY discard it (RFC 9990 Section 3.5).
If you delegate reporting, the delegate's record is part of your configuration even
though it lives in someone else's zone. When reports stop after a provider migration,
the missing _report._dmarc TXT record at the new host is the first thing to
look for.
Failure reports (RFC 9991) and the fo tag
A failure report covers one individual message that failed DMARC evaluation, and carries as much of that message as the reporting party is prepared to disclose. RFC 9991 §2 describes them as generated and sent almost immediately after the mail receiver detects the failure, and states that they should include as much of the message header fields and body as possible, consistent with the reporting party's privacy policies. That is the whole point and the whole problem at once: an aggregate report tells you that 42 messages from one IP address failed, a failure report hands you one of those messages so you can see why.
Failure reports are optional for the receiver in a way aggregate reports are not. RFC 9989 §5.3.5 defines when a message fails DMARC, §5.3.8 says mail receivers SHOULD generate and send aggregate reports at least once every 24 hours, and §5.3.9 says only that they MAY choose to send failure reports — adding that receivers rightly concerned about protecting user privacy have either chosen to heavily redact the information in such reports, which can hinder their usefulness, or not send them at all.
Two tags in your DMARC record govern this, and they work as a pair. The ruf tag names the destinations; the fo tag states the conditions under which you want a report generated (RFC 9989 §4.7). The report format is the Abuse Reporting Format of RFC 6591, extended by RFC 9991 §4.
The four fo values
The fo tag defaults to 0. Values 0 and 1 are mutually exclusive; any other combination is written as a colon-separated list, and the values may appear in any order, so fo=0:d and fo=d:0 mean the same thing (RFC 9989 §4.7). Report generators may choose to adhere to the requested options — the tag is a request, not an instruction.
Value
Condition for generating a report (RFC 9989 §4.7)
Report type
What that means in practice
0 (default)
Generate a DMARC failure report if all underlying authentication mechanisms fail to produce an aligned pass result.
DMARC failure report
The narrow setting. A report is generated only when nothing worked: neither SPF nor DKIM produced an aligned pass. This is the same condition under which DMARC itself fails, so you receive reports only for messages that actually failed DMARC.
1
Generate a DMARC failure report if any underlying authentication mechanism fails to produce an aligned pass result.
DMARC failure report
The broad setting, and the one that is routinely described backwards. A message that produces an aligned DKIM pass but no aligned SPF pass passes DMARC, because one or more authenticated identifiers aligned with the author domain (§5.3.5) — and still generates a report under fo=1, because one of the two mechanisms did not produce an aligned pass. Expect reports about mail that was delivered normally.
d
Generate a DKIM failure report if the message had a signature that failed evaluation, regardless of its alignment.
DKIM failure report (RFC 6651)
A different report type, not a DMARC report. The trigger is a broken signature, and alignment is explicitly irrelevant: a failed signature from a domain unrelated to yours still qualifies.
s
Generate an SPF failure report if the message failed SPF evaluation, regardless of its alignment.
SPF failure report (RFC 6652)
Again a separate report type, again with alignment explicitly excluded from the condition. The message failed SPF; whether the SPF identifier had anything to do with your author domain does not enter into it.
The direction of 0 and 1 is worth reading twice, because commercial documentation frequently swaps them. 0 requires that all mechanisms fail before a report is generated; 1 requires only that any one of them fails. fo=1 is therefore the setting that produces strictly more reports than the default, including reports about messages that passed DMARC.
For d and s, RFC 9991 §3 makes the relationship explicit: a mail receiver that generates DMARC failure reports may choose to issue failure reports specific to the authentication mechanism that failed, instead of or in addition to the DMARC failure report, and it determines which types to transmit based on its own policy, the failure in question, and the content of the fo tag. The same reports can also be requested independently of DMARC through the mechanisms in RFC 6651 and RFC 6652.
fo is ignored completely without ruf
This is the single most common way a failure-reporting configuration ends up doing nothing. RFC 9989 §4.7 states it as a hard condition: the content of the fo tag MUST be ignored if a ruf tag is not also specified. And the ruf tag itself carries the corresponding rule — if it is not provided, mail receivers MUST NOT generate failure reports for the domain.
No failure reports at all. There is no ruf tag, so the fo tag is ignored and failure reports MUST NOT be generated. The record looks configured for failure reporting and is not.
All three conditions are requested: DMARC failure reports whenever any mechanism fails to produce an aligned pass, plus DKIM-specific and SPF-specific reports.
Note that fo=0:1 is not valid: the two values are mutually exclusive (RFC 9989 §4.7).
Why most large receivers send none of these
A failure report can contain the entire message. RFC 9991 §7.1 states that failure reports may include personally identifiable information and non-public information from messages that fail to authenticate, since these reports may contain message content as well as trace header fields, and that although the reporting format supports redaction (RFC 6590), failed-message reporting is capable of exposing the entire message to the report consumer. RFC 9991 §7 concludes from this that many large-scale providers limit or entirely disable the generation of failure reports, preferring to rely on aggregate reports, which provide statistical visibility without exposing sensitive content. RFC 9989 §10.2 says the same thing from the other side: due to the nature of the email contents that may be shared through failure reports, most mail receivers refuse to send them out of privacy concerns, and out-of-band agreements between report consumers and mail receivers may be required. Publishing ruf and receiving nothing is the normal outcome, not a misconfiguration — and the reports you do receive may be heavily redacted, which RFC 9989 §5.3.9 acknowledges can hinder their usefulness.
The exposure is not limited to your own mail. RFC 9991 §7.1 gives two consequences that surprise most operators. First, a domain owner requesting reports receives information about mail using its domain that it did not cause to be sent — valuable insight into abusive content, but also potentially PII or non-public information from legitimate messages that failed authentication by accident. Second, information about the final destination of mail, where it would otherwise be obscured by intermediate systems, can be exposed: the RFC's cited example is the exposure of mailing list members when one list member posts and failure reports are generated as that message is delivered to the others. Forwarding arrangements have the same effect, making destinations previously unknown to the domain owner visible.
RFC 9991 §7 lists what operators who do enable failure reporting are strongly encouraged to do: limit the scope and duration of use to targeted diagnostic activities, control and validate the reporting URIs carefully, apply minimisation techniques such as redaction of message bodies and header fields, and always transmit reports over secure channels. §7.3 adds the handling requirements on the receiving end, because partially redacted or unredacted reports may propagate spam, phishing, or malware content: isolate report streams from other mail streams, evaluate reports in sandboxes, use network segmentation, and limit access to authorised individuals with appropriate security training.
There is a second-order risk in the DNS itself. RFC 9989 §11.6 notes that the addresses in the ruf tag receive more information that might be considered private data, which makes those URIs more attractive targets for intrusion attempts than the ones in rua, and that attacking the DNS of the subject domain in order to route failure data to an attacker's systems may be an attractive prospect; deployment of DNSSEC is advisable where this is a concern.
PSDs must not ask for failure reports
Two rules apply to public suffix domains, and they sit in different documents.
RFC 9991 §2, addressed to the report generator: report generators MUST NOT consider ruf tags in DMARC policy records that have a psd=y tag, unless there are specific agreements between the interested parties. A PSD record carrying ruf is therefore inert by default at every conforming receiver.
RFC 9989 §10.2, addressed to the publisher: DMARC policy records for multi-organizational PSDs MUST NOT include the ruf tag at all.
The reasoning is the one RFC 9989 §10.2 gives for failure reports generally: they provide insight into email sending patterns down to specific users, and before failure reports are requested at all, any such data spillage risks have to be addressed through data management controls. The corresponding rule for aggregate reports is weaker — §10.1 warns that multi-organizational PSDs which do not mandate DMARC use by registrants risk exposure of registrant private data if they include rua, but it is a warning, not a prohibition.
What the report itself contains
RFC 9991 §4 augments the RFC 6591 reporting format with a set of Abuse Reporting Format header fields specific to DMARC. Knowing which of these are required is what lets you tell a conforming report from a partial one.
ARF header field
Requirement level (RFC 9991 §4)
Content
Identity-Alignment
REQUIRED
A comma-separated list of the authentication mechanism names that failed to authenticate an aligned identity, or the keyword none if all attempted methods succeeded at authenticating an aligned identity. The permitted method names are dkim and spf, each at most once.
Delivery-Result
OPTIONAL
The disposition the receiver applied to the message.
DKIM-Domain, DKIM-Identity, DKIM-Selector
REQUIRED for DKIM failures of an aligned identifier
Which signature failed, identified by signing domain, identity, and selector.
The canonicalised material the verifier actually hashed, which is what you need to find a signature broken in transit.
SPF-DNS
REQUIRED for SPF failure of an aligned identifier
The DNS data the SPF evaluation was based on.
RFC 9991 §4 also defines the authentication failure type dmarc for the ARF Auth-Failure field, used when a failure report is generated because some or all of the authentication mechanisms failed to produce aligned identifiers.
Two operational constraints on the sending side. Report generators MUST implement a rate limit on outgoing reports so as not to flood report consumers, which would otherwise allow a denial-of-service attack (RFC 9991 §2 and §8.1) — an attacker sending large volumes of mail that fails both SPF and DKIM while purporting to be from your domain turns every participating receiver into a source of traffic aimed at your reporting address. RFC 9991 §8.1 recommends aggregating such reports using the ARF Incidents field, storing reports briefly to consolidate like incidents, and rate limiting with the remainder discarded. Separately, RFC 9991 §5.1 states that email streams carrying DMARC failure reports SHOULD be DMARC-aligned, since unaligned reports may themselves produce further failure reports and cause mail loops.
Sending reports to another domain: the _report._dmarc authorisation
Nothing in the DNS stops you from publishing rua=mailto:reports@thirdparty.example.net in your own DMARC record. That is exactly the problem RFC 9990 §4 exists to solve. Without a check, a bad actor could publish a DMARC policy record requesting that reports be sent to a victim's address and then send a large volume of mail that fails both DKIM and SPF to a wide variety of destinations; the victim would in turn be flooded with unwanted reports. The receiving domain therefore has to consent, and it consents in its own DNS.
When the check is triggered
The trigger is a comparison of organizational domains, not of hostnames. RFC 9990 §4: when a mail receiver discovers a DMARC policy record, and the organizational domain at which that record was discovered is not identical to the organizational domain of the host part of the authority component of the URI in the rua tag, the verification steps must be taken.
Record at
rua destination
Authorisation needed?
_dmarc.example.com
mailto:dmarc-feedback@example.com
No. Same organizational domain.
_dmarc.example.com
mailto:dmarc-feedback@reports.example.com
No. The host differs, but its organizational domain is still example.com.
_dmarc.example.com
mailto:auth-reports@thirdparty.example.net
Yes. Different organizational domain.
_dmarc.mail.example.com
mailto:auth-reports@thirdparty.example.net
Yes — and note that the label prepended in step 3 below is the domain the policy was retrieved from, which is not necessarily the organizational domain used in this comparison.
How the name is constructed
RFC 9990 §4 gives the construction as an ordered procedure:
Extract the host portion of the authority component of the URI. This is the destination host, since it refers to a report receiver.
Prepend the string _report._dmarc.
Prepend the domain name from which the policy was retrieved, after conversion to an A-label if needed.
If the length of the constructed name exceeds DNS limits, a positive determination of the external reporting relationship cannot be made; stop.
Query the DNS for a TXT record at the constructed name. If the result is a temporary DNS error such as a timeout, the mail receiver may elect to temporarily fail the delivery so the verification test can be repeated later.
Worked through with the example from RFC 9989 Appendix B.2.3, where example.com asks a third party to receive its failure reports:
The two records in full
; published by example.com
_dmarc.example.com. IN TXT ( "v=DMARC1; p=none; "
"rua=mailto:dmarc-feedback@example.com; "
"ruf=mailto:auth-reports@thirdparty.example.net" )
; published by the report consumer, in the zone thirdparty.example.net
example.com._report._dmarc.thirdparty.example.net. IN TXT "v=DMARC1;"
The name the receiver queries is assembled from the destination host thirdparty.example.net, the fixed labels _report._dmarc, and the domain the policy came from, example.com. The authorising record needs nothing beyond v=DMARC1 to grant consent.
The authorising record is published by the recipient of the reports, in the recipient's zone. You cannot create it yourself, and a report processor that has not published it will not receive your reports no matter what your DMARC record says — where the algorithm fails to confirm authorisation, the URI MUST be ignored by the mail receiver generating the report (RFC 9990 §4).
What counts as a valid authorisation record
Steps 6 to 8 of RFC 9990 §4 define the acceptance test, and it is deliberately minimal. For each TXT record returned, the result is parsed as a series of tag=value pairs in the same overall format as a DMARC policy record; the v=DMARC1 tag is mandatory and MUST appear first in the list, and any record failing that test is discarded. A trailing semicolon is optional. If no returned TXT record passes basic parsing, a positive determination cannot be made and the procedure stops. If at least one record remains after parsing, the external reporting arrangement was authorised by the report consumer.
A passing record establishes
That the destination host consents to receive reports about the specific domain named in the leftmost labels.
That the mail receiver may deliver the report to the URI in your DMARC record.
Optionally, a replacement URI chosen by the destination (see below).
A passing record does not establish
Any policy for the domain named in the leftmost labels. The p tag is not applicable to third-party reporting records (RFC 9989 §4.7).
That reports will in fact arrive; delivery to the URI can still fail, and the receiver may then cache the data and retry or discard it (RFC 9990 §3.5).
Anything about failure reports, unless the same procedure is repeated for ruf.
A report consumer willing to receive reports for any domain can use a wildcard: a TXT record at *._report._dmarc.example.net containing at least v=DMARC1 confirms that example.net is willing to receive DMARC reports for any domain (RFC 9990 §4). If the consumer is overcome by volume it can simply remove the confirming record, though because of positive caching the change takes effect only after the record's TTL has elapsed.
The length limit
Step 4 of the procedure is a real failure mode rather than a formality, because the constructed name is the concatenation of two full domain names plus two fixed labels. RFC 9990 §4 states only that if the length exceeds DNS limits, a positive determination of the external reporting relationship cannot be made and the procedure stops — which, combined with the rule that an unconfirmed URI MUST be ignored, means the reports are silently not sent. The RFC's own advice for this case is that the domain owner may need to consider using a shorter domain name, or coordinate with another party that may allow for a shorter DNS label. A report processor offering a short reporting hostname is doing this for a reason.
When the authorisation record redirects the destination
Step 9 of RFC 9990 §4 lets the destination override where the reports go. If a rua tag is discovered in the authorisation record, the mail receiver replaces the value taken from your DMARC record with the one found in the authorisation record. This permits the report consumer to override the report destination — useful when the processor has changed its mailbox, or wants to be robust against a typo in a customer's DMARC record (RFC 9989 Appendix B.2.4).
Two limits bound this, both aimed at loops and indirect abuse:
The override must stay on the same host. RFC 9990 §4: to prevent loops or indirect abuse, the overriding URI MUST use the same destination host from the first step. A processor can redirect reports@thirdparty.example.net to aggregate-reports@thirdparty.example.net; it cannot redirect it to another domain.
A cross-domain override poisons both URIs. If the confirming record includes a URI whose host is again different from the domain publishing that override, the mail receiver MUST NOT generate a report to either the original or the override URI. The attempted redirect does not merely fail — it costs you the original destination as well.
An overriding authorisation record
; zone file for thirdparty.example.net
; accept DMARC reports on behalf of example.com and override the mailboxes
example.com._report._dmarc.thirdparty.example.net. IN TXT (
"v=DMARC1; "
"rua=mailto:aggregate-reports@thirdparty.example.net; "
"ruf=mailto:failure-reports@thirdparty.example.net" )
Both override URIs use the same host as the destination host extracted in step 1, so both are permitted. Reports that example.com addressed to any mailbox at thirdparty.example.net are redirected to these two.
Changing either of those URIs to a host outside thirdparty.example.net would not redirect the reports elsewhere. It would stop them being generated at all, to either address.
The same procedure applies to ruf
RFC 9991 §5 does not define a separate mechanism. A mail receiver generating failure reports for an external destination MUST use the Verifying External Destinations procedure of RFC 9990 §4, substituting the ruf tag wherever the rua tag appears in that procedure. The constructed name is identical — the _report._dmarc label pair carries both report types, and a single authorisation record covers rua and ruf alike unless it overrides one of them.
RFC 9991 §5 gives the same rationale in both directions: the check prevents a bad actor from publishing a DMARC policy record requesting failure reports to an external destination and then deliberately generating those reports as a form of abuse, and it prevents a domain owner from unilaterally forcing an external destination to deal with unwanted messages and potential privacy issues. Given what §7.1 says about the contents of a failure report, the second half of that sentence matters as much as the first.
RFC 9989 §11.6 adds an operational note worth acting on: where possible, external reporting is best achieved by having the report directed to a domain that can receive mail and simply forwarding it automatically to the desired external destination. That sidesteps the authorisation record, the length limit, and the extra DNS load the verification places on the claimed external recipient.
Our scanner runs this procedure for the mailto: destinations of your rua and ruf tags — the first eight distinct destination hosts, counted before the external/internal decision is made. A reporting URI with any other scheme is not resolved here: mailto: is the only scheme a report generator is required to support, and RFC 9990 §3.5.3 leaves further schemes to later versions. For each of those hosts it determines the organizational domain, constructs the _report._dmarc name for the ones that turn out to be external, checks the name against the DNS length limit, looks up the authorisation record, and applies the step 9 rule that an override pointing at a different host stops the reports entirely. See the dmarc-rua-ext check.
Troubleshooting: a decision tree
Almost every DMARC problem reported in practice is one of a small number of causes, and the fastest way to the right one is to start from the symptom rather than from the record. Pick the symptom below, then work down the branches: each one ends with what the failure looks like, how to confirm it, and what to change. The relevant RFC 9989, RFC 9990 and RFC 9991 sections are named where the behaviour is normative, so you can check the reasoning rather than take it on trust.
Two things are worth having open while you work through this. The domain scan shows which record actually applies to the name you are asking about, including one inherited from further up the tree walk, and the sender test shows what a receiver sees for a message you really sent: which identifier passed, which domain it was aligned against, and which record governed the decision.
1. Legitimate mail is being rejected or filed as spam
SPF passes, but DMARC still fails
The message shows spf=pass in the receiver's Authentication-Results header field, and dmarc=fail right next to it. This is one of the most common DMARC surprises, and it is not a contradiction: DMARC relies solely on SPF validation of the MAIL FROM identity, and the domain that passes SPF must then also be in alignment with the domain in the From: header field (RFC 9989 §4.4.2). A mail platform that sets its own bounce domain — a Return-Path: of bounces.example.net under a From: of example.com — produces exactly this result.
Send a message through the affected path to the sender test. It names the authenticated identifier, the author domain, and whether the two aligned; the same comparison can be read out of a delivered message by putting Return-Path: and From: side by side, or out of an aggregate report by comparing the SPF domain in a row with the header-from domain of the report.
Either make the envelope domain align — most platforms offer a custom return path under your own domain — or get an aligned DKIM signature onto the stream, which is the more durable fix. Only one aligned identifier has to pass: a message is a DMARC pass if any DKIM-authenticated identifier aligns with the author domain (§4.4.1), regardless of what SPF did.
The mail was forwarded, or went through a mailing list
Failures cluster on particular recipient domains rather than on a sending system of yours, and the source addresses in the aggregate reports belong to a forwarder or a list server. RFC 9989 §7.4 describes the mechanism: when an alumni forwarder or a role address relays your message, the connecting IP address is the relay's, so SPF fails unless the relay rewrote the MAIL FROM address, while DKIM signatures generally survive the relay untouched.
Check whether an aligned DKIM signature exists at all on that stream — the sender test verifies every signature on a message you send and reports the signing domain of each. If the only thing carrying the stream was SPF, forwarding will break it every time.
Sign your mail. RFC 9989 §7.4 is explicit that domains publishing p=reject MUST NOT rely solely on SPF to secure a DMARC pass and MUST apply valid DKIM signatures to their messages. If the affected domain hosts general users who post to Internet mailing lists, the same section advises that such domains SHOULD NOT publish p=reject at all; the mailing list section below covers what the list does to the message and what ARC can and cannot repair.
An SPF hard fail rejects the message before DMARC is ever evaluated
The rejection happens early in the SMTP transaction and the reply text mentions SPF, not DMARC. Worse, the affected mail is missing from your aggregate reports entirely, so the volume looks smaller than it is.
Look at your SPF record for the sending domain: if it ends in -all, a receiver may reject a message that is not covered by it before any DMARC processing takes place. RFC 9989 §7.1 spells out both consequences — a message that would otherwise pass DMARC through an aligned DKIM signature can be rejected solely on the SPF fail, and because the transaction never reaches the DATA phase, the From: domain is never revealed and the failure never appears in an aggregate report.
Find the sending source that is missing from the SPF record and add it, or move to a soft fail while you remediate. An aligned DKIM signature does not rescue a message that was refused before DATA, so with -all in place every legitimate source has to be listed. The domain scan evaluates the SPF record including its lookup count.
DMARC passes and the mail is still filtered
The receiver's Authentication-Results header field says dmarc=pass, and the message is in the spam folder anyway. Nothing is broken.
Confirm the pass in the sender test and then stop looking at DMARC. RFC 9989 §5.4 states that a DMARC pass indicates only that the use of the author domain has been validated as authorized by the domain owner; it carries no explicit or implicit value assertion about the message or the domain owner, and mail receivers MAY reject or quarantine a message even when it passes.
There is no tag you can add to change this outcome. What is left is everything DMARC does not measure: content, sending reputation, list hygiene, and the technical hygiene of the rest of the domain. The domain scan covers the last of those.
2. No reports are arriving
The record has no rua tag
The record contains a policy and nothing else — often the case when it was copied from an example or generated by a control panel that treats reporting as optional.
Query the TXT record at _dmarc.example.com with the DNS lookup and read the tag list. If no rua appears, the answer is settled: if the tag is not provided, mail receivers MUST NOT generate aggregate feedback reports for the domain (RFC 9989 §4.7).
Add a rua=mailto: address pointing at a mailbox that is prepared to consume machine-generated reports (§5.1.3, §5.1.4). The DMARC generator produces the complete record.
Reports go to another domain that never authorised it
The rua address is at a report processor or at a second domain of yours — rua=mailto:dmarc@example.net in the record at example.com — and reports never appear. There is no bounce and no error anywhere, because the receiver's check happens in DNS before a report is ever generated.
When the organizational domain of the record does not match the organizational domain in the reporting URI, the mail receiver MUST verify the arrangement by querying a TXT record at the destination host with _report._dmarc and the policy domain prepended (RFC 9990 §4). For this example the name is example.com._report._dmarc.example.net, and it must return a record starting with v=DMARC1. Query that exact name with the DNS lookup. A processor that accepts reports for anyone can publish a wildcard instead, at *._report._dmarc.example.net.
Have the destination domain publish the authorisation record; where the algorithm fails to confirm the arrangement, the URI MUST be ignored by the mail receiver (RFC 9990 §4). Note that the confirming record may also override the destination, but only to a URI with the same destination host. The domain scan performs this check for every external mailto: reporting address in your record. The DMARC generator answers the same question for a record you load into it: per destination it states whether the authorisation record was found, was absent, or could not be checked, and prints the record the receiving side has to publish.
The reports are sent, but your own mailbox never shows them
The reporting address is inside your own domain, so no external authorisation is needed, and the mailbox is still empty — or full of things you did not recognise as reports.
Search that mailbox for the shape the RFC prescribes rather than for the word DMARC. The subject line follows a fixed form — Report Domain: example.com Submitter: mail.receiver.example Report-ID: … — and the report itself is an attachment named receiver!policy-domain!begin-timestamp!end-timestamp.xml.gz, usually GZIP-compressed (RFC 9990 §3.5.2). If a gateway strips compressed attachments or the mailbox enforces a small size limit, the reports are gone before you see them; the same section notes that a report can exceed the receiver's SMTP size limit outright.
Deliver that address to a mailbox that accepts .gz attachments and generous message sizes, and keep it out of aggressive filtering. If delivery is not possible because the advertised service cannot accept the reports, a mail receiver MAY discard the data rather than retry (RFC 9990 §3.5), so a broken report mailbox costs you the data permanently.
You are waiting for failure reports, not aggregate reports
Aggregate reports arrive, the ruf mailbox stays empty, and nothing indicates why.
The two report types have different economics. Aggregate reports come as daily or more frequent feedback from mail receivers (RFC 9990 §3.1). Failure reports are generated almost immediately after a failure is detected, but only where the mail receiver is willing to provide them (RFC 9991 §2) — and RFC 9991 §7 records that many large-scale providers limit or entirely disable failure report generation, because such a report can expose the header fields or the entire content of a message and with them personally identifiable information. An external ruf destination additionally needs the same _report._dmarc authorisation as rua, with the ruf tag substituted in the procedure (RFC 9991 §5).
Build your monitoring on aggregate reports and treat failure reports as an occasional diagnostic bonus. There is also no way to ask for a different reporting cadence any more: the ri tag that requested an interval between aggregate reports was removed from the standard (RFC 9989 Appendix C.5.2).
3. The record is not found or seems to be ignored
v=DMARC1 is not spelled exactly that way, or is not the first tag
The record is visibly there in DNS, most checkers report that the domain has no DMARC record, and no receiver acts on it.
Read the version tag character by character — or let the domain scan do it: where a TXT record at _dmarc fails only the case of DMARC1, it says so explicitly and shows the record, whether or not a policy from further up the tree still applies. Its value is case sensitive, the only possible value is DMARC1, and the tag MUST be the first in the list; if the tag is absent, not first, or carries any other value, the entire record MUST be ignored (RFC 9989 §4.7). A record beginning v=dmarc1, or one that starts p=reject; v=DMARC1, is discarded whole — not partially applied. The same rule governs the tree walk: records that do not start with a v tag identifying the current version are discarded at each step (§4.10).
Republish the record with v=DMARC1 as the first tag, in exactly that case. The DMARC generator emits the tags in a valid order.
There is more than one DMARC record at the name
Two TXT records at _dmarc.example.com both begin with v=DMARC1. This happens after a migration, or when a mail platform adds a record next to the one you already published. That name now behaves as though it carried no DMARC record of its own — and that is worse than either of the two records alone. On a subdomain it is not “no DMARC”: the tree walk carries on upward and hands the decision to whichever record it finds above you, usually the organizational domain’s sp or np. At the organizational domain itself it usually does mean no policy at all — only a PSD record above it could still supply one.
Query TXT at _dmarc.example.com with the DNS lookup, which shows every answer from several independent resolvers, so a record that only some views return is visible too — and, where the zone’s nameservers can be determined, a second panel lists their answers one by one, with the AA flag and the SOA serial of each. That second panel is the one that settles a duplicate left over from a migration, because resolvers answer from one cached view and can agree while a single one of your own nameservers still serves the extra record. The rule is unforgiving: if multiple DMARC policy records are returned for a single target, they are all discarded (RFC 9989 §4.10, steps 2 and 6).
Delete all but one record, merging the tags you need into the survivor. In the domain scan this condition caps the email module at grade C whether or not a record further up the tree still governs the name: the policy you published at this name is gone either way, and which one replaces it is not something you chose.
The record is published under the wrong name
The record content is correct but sits at example.com itself, or at _dmarc.example.com.example.com because the DNS interface appended the zone name to a value that already contained it.
DMARC policy records are stored as TXT records with names starting with the _dmarc label, so the record for example.com lives at _dmarc.example.com and is found by a TXT query for that name (RFC 9989 §4.5). Query both the intended name and the doubled name with the DNS lookup; seeing the record answer at the doubled name identifies the cause immediately.
Publish the record under the _dmarc label of the domain that appears in the From: header field. If your DNS interface appends the zone automatically, enter only the _dmarc label; if it does not, enter the fully qualified name.
The record parses, but carries no usable policy tag
The record looks complete and receivers treat the domain as if it were in monitoring mode, or ignore it entirely. A misspelled tag name is the usual cause, because only tags in the IANA registry are processed and unknown tags MUST be ignored (RFC 9989 §4.7) — policy=reject is not an error, it is invisible.
Compare every tag name against the registry list, and watch the fallback rule that decides what happens next: if a retrieved record does not contain a valid p tag, or contains an invalid sp or np tag, then the receiver acts as if p=none had been retrieved when a syntactically valid reporting URI is present in rua, and otherwise applies no DMARC processing to the message at all (§4.10.1).
Correct the tag names and values so the intended policy is carried by a valid p tag. Building the record in the DMARC generator avoids the whole class of problem, since it can only emit registered tags.
4. Only mail from a subdomain fails
The organizational domain's policy applies to the subdomain
You published p=reject at example.com and mail from newsletter.example.com started being rejected, although you never published anything for the subdomain.
That is the defined behaviour: the policy applies to the domain queried and to subdomains, unless the subdomain policy is explicitly described using the sp or np tags (RFC 9989 §4.7). When no record exists at the author domain, policy discovery walks up the tree and the policy is taken from the sp tag if the author domain exists, or the np tag if it does not, falling back to p when neither is present (§4.10.1). Run the domain scan against the subdomain itself to see which record was found and which policy it produced.
Decide where the exception belongs. Either add sp= to the organizational domain record to set a different policy for all subdomains, or publish a record at _dmarc.newsletter.example.com, which policy discovery prefers over the organizational domain's record (§4.10.1) — the second option keeps the exception scoped to the one stream and gives it separate reports.
The subdomain's own record carries sp= and it is being ignored
You published v=DMARC1; p=none; sp=reject at _dmarc.newsletter.example.com and the sp half has no effect on anything.
The sp tag will be ignored for DMARC policy records published on subdomains of organizational domains and PSDs, as a consequence of policy discovery (RFC 9989 §4.7): discovery finds the record at the author domain itself and takes the policy from its p tag (§4.10.1), so this record is never treated as the subdomain policy of anything.
Put the policy you want for this name into its p tag, and leave sp and np to the record at the organizational domain, which is the only place they take effect.
np= is not covering the names you expected
You set np=reject to protect names you never use, and mail from one of them is still handled under a weaker policy — or the reverse: a name you consider retired is being treated as existing.
The np tag applies only to non-existent subdomains of the organizational domain queried, and not to existing subdomains or the domain itself (RFC 9989 §4.7). Existence here is broader than mail people expect: DMARC requires no particular record type, and if any resource record exists for a name, the domain exists; only an NXDOMAIN response means it does not (Appendix A.4, §3.2.13). A forgotten A record or a stray CNAME on old.example.com is enough to move that name out of np and under sp. Query the name for several record types with the DNS lookup to see which of the two it is.
Remove the leftover records if the name is genuinely unused, or set sp to the policy you intended for it. Because an NXDOMAIN answer means every name below it does not exist either (Appendix A.4), cleaning up one label can settle a whole branch at once.
Strict alignment breaks the subdomain that relaxed alignment allowed
Mail from news.example.com fails while mail from example.com passes, and the difference appeared when adkim=s or aspf=s was added.
Under relaxed alignment the authenticated identifier only has to share an organizational domain with the author domain; under strict alignment the two must be identical (RFC 9989 §4.4). The table in that section makes the case concrete: foo.example.com and news.example.com align in relaxed mode and not in strict mode. Send a message from an address at the affected subdomain through the sender test to see which domain actually signed it and against what it was compared.
Either sign with a DKIM key whose d= equals the author domain exactly and set the envelope domain to match, or drop back to relaxed alignment, which the RFC notes nearly all domain owners have found sufficient (§4.4).
Mailing lists, forwarding and ARC
DMARC has one structural weakness, and it is not cryptographic: the protocol was designed for mail that travels from the author's system to the recipient's system, and a large share of legitimate mail does not. RFC 9989 §7.4 names the affected paths — alumni forwarders, role-based aliases and mailing lists — and states plainly that p=reject can be incompatible with them and cause interoperability problems.
Why a list breaks authentication
The two mechanisms fail differently along an indirect path. SPF fails at the first hop that is not yours: the connecting IP address belongs to the forwarder, and unless that relay rewrites the MAIL FROM address the SPF check is performed against your domain from someone else's network (§7.4). DKIM usually survives plain forwarding — RFC 9989 notes that DKIM signatures will generally remain valid in these relay situations — which is exactly why the same section requires that domains publishing p=reject MUST NOT rely solely on SPF and MUST apply valid DKIM signatures to their messages.
A mailing list is not plain forwarding, though. List software routinely modifies the message: a tag prepended to the Subject: field, a footer appended to the body, sometimes a change of content transfer encoding or a header the list adds for its own bookkeeping. Every one of those touches material that the DKIM signature covers, so the signature that survived the forwarder does not survive the list, and both identifiers are now gone. Whether a given signature breaks depends on which header fields the signer covered and whether the body was signed in full, which is why one list can break your mail and another can leave it intact.
The consequence is not confined to the sender. RFC 9989 §7.4 describes what happens when a receiver honours p=reject on list traffic without further analysis: the rejection looks to the list software like a dead address, and the subscriber gets unsubscribed automatically. That is why the same section requires that mail receivers MUST NOT reject incoming messages solely on the basis of a p=reject policy, and that in the absence of other knowledge and analysis they MUST treat such failing mail as if the policy were p=quarantine.
RFC 9989 §7.4 also records what actually happens in practice: few mail receivers apply any mitigation for indirect flows, few organizations consider the effect of their policy on their users' indirect mail, and mail forwarded through mailing lists with an unmodified From: header field is frequently rejected under p=reject. Do not plan around the mitigation being applied.
Because of that, list software has adapted rather than waited. RFC 9989 §7.4 describes the workarounds as firmly established and treated by list operators as a fact of life: in the ten years since large consumer mail systems started publishing p=reject, list software has adopted rewriting of the From: header field so that it is DMARC aligned — some using the list's own address, others a reversible per-address form such as bob=example.com@user.somelist.example. Your domain then no longer appears as the author of the message, which is the price the list pays to deliver it, and one reason RFC 9989 §7.4 advises that domains hosting users who post to mailing lists SHOULD NOT publish p=reject, and that those who do should tell their users their list participation may be hindered.
ARC: carrying the earlier verdict forward
The Authenticated Received Chain (ARC, RFC 8617) attacks the problem from the other side. Rather than trying to keep the original signature intact, an intermediary records the authentication results it saw at the moment it received the message, seals that record cryptographically, and passes it along. A downstream receiver that finds a chain in which every hop's seal validates can see what SPF, DKIM and DMARC produced before the message was modified, and can decide to accept a message whose own DMARC evaluation now fails.
The limitation is where the whole idea rests: an ARC chain is an assertion by the intermediary, not a proof about the message as it now stands. A valid chain says that a particular sealing domain claims a particular verdict was seen — it does not say that domain is honest. A receiver therefore only benefits from ARC if it trusts the sealing domains in the chain, and building that trust is a matter of the receiver's local policy, not something the sender can publish. An unknown intermediary's flawless chain buys you nothing at a receiver that has no reason to trust it.
Be equally careful about how much ARC is deployed. RFC 9989 §7.4 states that mail developers have been trying for a decade to invent technical methods that let mailing lists work without modifying the From: header field, with ARC as a prominent example, and that as of publication none of these methods has become widely used — the document adds that it can be updated if one does. Treat ARC as something that helps where the receiver already participates, not as a reason to publish a stricter policy than your indirect mail flows can carry.
If you want to see a real chain rather than reason about one, the sender test verifies ARC chains on the message you send: it lists every hop with its sealing domain, and reports separately whether the seal and the message signature at that hop validate cryptographically. That distinction matters when you operate a forwarding hop yourself, because it separates a chain you broke from one that arrived broken.
DMARC and BIMI
BIMI — Brand Indicators for Message Identification — lets a domain owner publish an indicator that participating mail clients may display next to authenticated messages. It is not part of DMARC and is specified separately, in the Internet-Draft draft-brand-indicators-for-message-identification (revision 14, 1 May 2026, which replaced the earlier draft-blank-ietf-bimi), which is still a draft and still references the older DMARC specification RFC 7489 rather than RFC 9989. What makes it relevant here is that BIMI has no authentication of its own: it borrows DMARC's, and it only borrows it where the policy is actually being enforced.
The draft states the prerequisite in its introduction: to participate in BIMI, domain owners must have a strong DMARC policy — quarantine or reject — on both the organizational domain and the From: domain of the message. Its receiver-side requirements in §7.1 turn that into concrete conditions a message has to satisfy before BIMI processing may be performed at all:
The DMARC result for the author domain must be pass; if it is not, and no additional authentication method salvages it, BIMI processing MUST NOT be performed.
If the policy for the author domain or the author organizational domain is p=none, BIMI processing MUST NOT be performed.
If the record includes a subdomain policy and that policy is sp=none, BIMI processing MUST NOT be performed.
If the policy is p=quarantine and the record defines a percentage tag, that tag MUST be pct=100, otherwise BIMI processing MUST NOT be performed.
Read the last condition precisely, because it is routinely repeated in a form that is wrong. The requirement is conditional: if such a tag is present, its value must be 100. Nothing in the specification asks you to add a pct tag, and there is no reason to have one. RFC 9989 removed pct from the standard altogether (Appendix C.5.2), the IANA DMARC Tags registry now lists it as historic (§9.3), and unknown tags MUST be ignored by receivers that follow the current specification (§4.7). If your record still carries pct=100 from an older deployment it satisfies the BIMI condition and costs nothing; the cleaner record is the one without it.
There is a divergence worth knowing about, because it decides what tools tell you. The draft's condition is conditional and scoped to p=quarantine; the BIMI Group's implementation guide is not, and states flatly that ‘None’ policies or ‘pct’ less than 100 percent are not accepted. So a record reading p=reject; pct=25 satisfies the specification and fails the industry guidance. Our own scanner deliberately follows the stricter reading and reports such a record as not BIMI-eligible. That is a judgement call, not a fact about the protocol, and it rests on the costs being lopsided: deleting a pct tag is cheap but not free — conforming receivers ignore it, yet receivers still on RFC 7489 do not, so removing it lifts them to full enforcement and every sending path has to be aligned first — whereas a logo that never appears has already cost you a mark certificate. Nobody outside a mailbox provider can observe which rule a given inbox actually applies, so treat any confident claim in either direction — including ours — as a policy choice rather than a measurement.
One tag deserves a warning of its own, and it is one the BIMI draft does not mention at all: t=y. RFC 9989 §4.7 defines t=y as a request that the validating actor not apply the declared policy but instead treat failing messages one level below it — reject is applied as quarantine, and quarantine is applied as none. A record reading p=quarantine; t=y therefore asks receivers for exactly the handling that BIMI's own §7.1 rules out. Since the draft was written against RFC 7489, which had no t tag — and revision 14, dated 1 May 2026, still is throughout — it says nothing about how a receiver should read this combination, and our own tools do not leave it open. t=y counts as “DMARC not enforced” in the domain scan and in the Record Studio, so even p=reject; t=y is reported as not BIMI-eligible. Unlike the pct rule above, that is not us choosing between two published sources; it is our own judgement in a gap the draft leaves, and it rests on one idea: a rung of a rollout ladder is not a policy a logo should hang on. The way out is to finish the rollout, not to delete the rung.
A workable BIMI-ready DMARC record therefore has an enforcing p, no t=y, an sp that is either absent or not none, and no pct tag at all:
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc-reports@example.com"
The DMARC side is the prerequisite, not the whole job — the indicator itself, its SVG profile, and the Verified Mark Certificate that some mailbox providers require are separate matters. The BIMI check evaluates a published BIMI record together with the DMARC policy that has to back it, and the BIMI generator builds the record.
How DMARC fits with the rest of the mail stack
DMARC answers one question: may this sender use this domain in the From: header field? It says nothing about how the message travelled, and the transport protocols say nothing about who wrote it. Keeping the two apart is the difference between a diagnosis and a guess: MTA-STS and DANE cannot stop a spoofed sender, and DMARC cannot stop an intercepted connection.
SPF
Authorises the hosts that may use your domain in the envelope MAIL FROM address, and DMARC relies solely on SPF validation of that MAIL FROM identity (RFC 9989 §4.4.2). Its evaluation is capped at ten DNS-querying mechanisms per check (RFC 7208), and a record that exceeds the limit produces a PermError — which means no SPF pass, and therefore no SPF contribution to DMARC.
DKIM
Signs the message so that a signing domain can be authenticated cryptographically; a message is a DMARC pass if any DKIM-authenticated identifier aligns with the author domain (RFC 9989 §4.4.1). That "any" is why selector hygiene matters — every selector you publish is a full path to a pass, so a weak or forgotten key under your domain is a weak point for the whole domain, and it is the mechanism that survives forwarding when SPF does not (§7.4).
MTA-STS and TLS-RPT
MTA-STS lets your domain declare that inbound mail must be delivered over authenticated TLS to the MX hosts you name, which defends the connection against downgrade and interception — a different attack from the one DMARC addresses. TLS-RPT is its reporting counterpart, giving you daily reports on the TLS connections other senders made or failed to make to you.
DANE
Binds your MX hosts' TLS certificates to TLSA records in DNS, authenticated by DNSSEC, so a sending server can verify the certificate without relying on the public CA system. Like MTA-STS it protects the transport path to your mail servers; unlike MTA-STS it takes its trust from DNSSEC rather than from a policy file fetched over HTTPS.
No. p=none asks receivers for nothing at all — it only turns on reporting. It is the right first step, because it shows you which senders exist before you start blocking any of them, but as a permanent state it provides no protection against spoofing.
What is the difference between quarantine and reject?
With quarantine you ask receivers to treat a failing message as suspicious, which in practice usually means the spam folder. With reject you ask them to refuse it outright. Reject is the stronger signal, but it is a request, not a guarantee: RFC 9989 explicitly forbids receivers to reject on the basis of your policy alone.
Why does SPF pass but DMARC still fail?
Because DMARC does not ask whether SPF passed — it asks whether the domain SPF validated belongs to the domain in your From header. A mail provider that sends with its own bounce domain produces an SPF pass for that provider, not for you. The fix is either a custom bounce domain under your own name, or a DKIM signature with your domain in d=.
Is the pct tag still valid?
RFC 9989 removed it. Receivers on the current specification ignore it entirely; older ones still honour it. That split is the problem: a pct below 100 now means your policy applies to everything at some receivers and to a fraction at others. Remove the tag — but be aware that older receivers then fall back to their default of 100, so the policy suddenly covers all of your mail.
Does RFC 7489 still apply?
No. The RFC Editor marks RFC 7489 as obsolete, replaced by RFC 9989, RFC 9990 and RFC 9991. Worth knowing: RFC 7489 was Informational and an Independent Submission, while RFC 9989 is the first DMARC document on the IETF Standards Track. In practice, though, the deployed receivers are a mix of both, so a record should behave correctly under either.
Do I need DMARC on a domain that never sends mail?
Yes, and it is the easiest case to get right. A domain nobody sends from has no legitimate senders to break, so you can publish p=reject with sp=reject and np=reject immediately. Combine it with a Null MX record and an SPF record of v=spf1 -all.
What changes for me because of DMARCbis?
Three things in practice. The organizational domain is now found by a DNS tree walk instead of the Public Suffix List, which changes which record applies to a subdomain. The pct tag is gone, so a percentage rollout has to be done with the t=y test flag instead. And np= lets you set a separate policy for subdomains that do not exist, which is where a lot of spoofing starts.
Why am I not receiving any reports?
The most common cause is the authorisation record. If your rua address is on a different organizational domain, that domain has to publish a TXT record confirming it accepts reports for you. Without it receivers send nothing and tell you nothing. The second most common cause is a typo in the mailto: URI, which is silently ignored.
Can DMARC stop someone impersonating me from a different domain?
No, and this is the most commonly misunderstood limit. DMARC protects the domain in your From header. An attacker who registers a lookalike domain, or who puts your company name in the display name while sending from their own address, passes DMARC on their own domain. RFC 9989 names display-name attacks as out of scope.
Will p=reject break my mailing lists?
It can. Many lists rewrite the From header or modify the message, which invalidates your DKIM signature — a message that was legitimate then fails DMARC at the subscriber. RFC 9989 §7.4 is blunt about it: it calls it critical that domains hosting users who might post to mailing lists SHOULD NOT publish p=reject, and asks those who do anyway to run p=none and p=quarantine first and to warn their users that list participation may be hindered. ARC helps where receivers trust the sealing intermediary, but it is not universal.