dotvitals

DMARC record examples and tag reference

Updated ·10 min read

A DMARC record is a single DNS TXT record at _dmarc under your domain, written as semicolon-separated tag=value pairs. There are eleven tags in current use and you will rarely need more than four of them, but the defaults of the ones you leave out matter as much as the ones you write down.

DMARC was re-specified in May 2026 as RFC 9989, which obsoletes RFC 7489 and RFC 9091 and splits reporting into RFC 9990 for aggregate reports and RFC 9991 for failure reports. Three tags were removed in that revision and two others are frequently misunderstood, so this reference is organised around what each tag does now rather than what older articles describe.

Check yours now

The shape of a record

The record lives at _dmarc.example.com as a TXT record. v must be the first tag and must read exactly DMARC1; p, when present, is conventionally written second. Tag names are case-insensitive; the value of v is not.

Only one DMARC record may exist at that name. If a lookup returns two, receivers treat the domain as having no DMARC policy at all — the same outcome as publishing nothing, which is a considerably worse failure than a typo, because nothing about it is visible unless you look.

Everything after v is optional as far as the syntax is concerned. That is a trap: a record of v=DMARC1; p=none; with no reporting address is syntactically valid, asks receivers to do nothing, and tells you nothing about your mail. It is the configuration that most looks like progress while providing neither protection nor information.

The record most domains should end up with
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

v and p: the two that decide everything

v=DMARC1 identifies the record. It must come first, and a TXT record at _dmarc that does not begin with it is not a DMARC record, however much it looks like one.

p states what you are asking receivers to do with mail that fails DMARC, and takes one of three values. p=reject asks them to refuse it at the SMTP transaction. p=quarantine asks them to deliver it somewhere other than the inbox, usually the spam folder. p=none asks them to do nothing at all.

p=none is a monitoring stage, not a policy. It stops no spoofed mail, and our own DMARC check grades it as a failure rather than a warning for exactly that reason: a domain at p=none is as impersonable as a domain with no record, and the only thing the record buys is the reporting. It is a legitimate place to start and an indefensible place to stay. If you publish it, decide in advance when you will move off it.

If p is absent entirely, the specification treats the policy as none. Writing p=none and omitting p have the same effect; omitting it just makes the omission harder to spot in review.

sp and np: subdomains, and subdomains that do not exist

sp sets a different policy for subdomains. Without it, subdomains inherit whatever p says, which is usually what you want.

The case for setting it explicitly is sp=reject on a domain whose subdomains never send mail. Attackers do use subdomains of real domains — invoices.example.com is more convincing than a lookalike domain and costs an attacker nothing if the subdomain is unprotected. Setting sp=reject closes that while the apex is still working through its rollout.

The case against setting it is the mirror image: sp=none on a domain whose p is enforcing quietly exempts every subdomain from the policy, which is usually an accident inherited from an old template. Our checker reports that combination, because it reads as protection and is not.

np, which is new in RFC 9989, sets the policy for subdomains that do not exist at all. Since a non-existent subdomain can never have legitimate mail, np=reject is safe to publish immediately, at any stage of a rollout, and it closes off the most common spoofing pattern of all. If you set nothing else beyond p and rua, set this.

Enforcing on non-existent subdomains while the apex is still at quarantine
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:dmarc@example.com"

adkim and aspf: what counts as aligned

DMARC does not care that SPF or DKIM passed; it cares that they passed for a domain that matches the one a human sees in the From header. These two tags control how strict that match has to be, and both default to r, for relaxed.

Relaxed alignment accepts any subdomain of the same organisational domain. A message From: billing@example.com that was DKIM-signed by mail.example.com is aligned. Strict alignment — adkim=s or aspf=s — requires the domains to be identical.

Leave both relaxed unless you have a specific reason not to. Strict alignment breaks the common and useful pattern of giving each bulk sender its own subdomain, which is also the standard way out of the SPF ten-lookup limit. Strict is a control for domains that have deliberately centralised all sending and want to detect any deviation; it is not a general hardening step, and turning it on is a frequent cause of legitimate mail failing after a rollout that had otherwise been clean.

rua and ruf: where reports go

rua is the address for aggregate reports: a daily-ish XML summary from each participating receiver listing every source that sent mail claiming to be your domain, with volumes and authentication results. This is the tag that makes DMARC useful. Without it, you have published a policy and have no way of knowing whether it is about to break something.

ruf is the address for failure reports — individual failed messages, usually redacted. Most large receivers do not send them at all, for privacy reasons, so setting ruf mostly produces nothing. It is not harmful, but it is not the visibility people expect it to be, and the reports that do arrive contain recipient data you now have to handle.

Both take comma-separated lists of URIs, normally mailto:. You can append a size limit — rua=mailto:d@example.com!10m asks receivers not to send reports larger than ten megabytes.

The rule that catches everyone: if the reporting address is at a different domain than the one being protected, that other domain has to opt in, by publishing a TXT record naming you. Without it, compliant receivers refuse to send the reports and nothing anywhere reports an error. This is the usual reason a newly configured third-party DMARC dashboard stays empty.

Authorising an external reporting destination (RFC 9990 §4)
; published in the REPORTING domain's zone, not yours
example.com._report._dmarc.dmarc-vendor.example. 3600 IN TXT "v=DMARC1"

fo, psd and t

fo controls when failure reports are generated, and only matters if you set ruf. The default, fo=0, requests a report when the message fails DMARC outright. fo=1 requests one when any underlying mechanism fails even if DMARC overall passed, which is more informative and far noisier. fo=d and fo=s narrow it to DKIM or SPF failures respectively.

psd declares that the domain is a public suffix — a registry publishing policy on behalf of everything beneath it. Unless you operate a top-level domain or a public suffix, this is not for you and the default of u is correct.

t is the test-mode flag, new in RFC 9989, and it is the tag most worth knowing about because it is what the removed pct tag was reached for. With t=y, a receiver is told that you do not yet want the stated policy actually applied: the assessment runs and the reports are generated, but the policy one level below what you declared is what takes effect — reject becomes quarantine, quarantine becomes none, and a policy of none is unaffected. It lets you publish a record that says what you intend while behaving more cautiously, and remove two characters to make it real. Be aware that a record carrying t=y reads as enforcing to most tools, which grade the p value they find. Ours grades the effective policy instead and reports the tag as its own finding, so a t=y record will not be scored as though it enforced what it says. Treat it as a dated rollout step rather than a configuration, and remember that BIMI will not display a logo while it is set.

The tags RFC 9989 removed

Three tags from RFC 7489 are gone, and copying an older example that uses them is the most common way to end up with a record that is wrong in a way nothing obviously complains about.

pct asked receivers to apply the policy to only a percentage of failing mail, and was the standard way to ramp an enforcement rollout. It is removed. The problem with keeping it is not that it is merely obsolete: deployed receivers are now split, some still honouring it and others ignoring it and applying the policy in full, so a record carrying pct=25 has no predictable meaning — you cannot say which of your mail is enforced. Delete it. Do not replace it with pct=100 either; full application is exactly what an absent tag already means, and a leftover pct is one of the things that will keep a BIMI logo from displaying, because BIMI validators still read the tag.

rf specified the failure report format and ri the aggregate report interval. Both are removed; receivers use the standard format and send aggregate reports on their own schedule, which in practice has always been roughly daily regardless of what ri asked for.

If you are staging a rollout and miss pct, the replacements are the policy level itself, sp and np for splitting apex from subdomains, and t=y as described above.

Worked examples

Four records covering the situations most domains are actually in. Each is complete and publishable as written, with the domain and reporting address changed.

The first is a monitoring stage: no protection whatsoever, and honest about it. The second is the safe enforcement step. The third is the destination. The fourth is a domain that sends no mail at all — a parked domain, a brand you have registered defensively — which should be locked down immediately rather than staged, because there is no legitimate mail to break. A no-mail domain should also carry a null MX and an SPF record authorising nobody.

Four complete records
; 1. monitoring only - collects reports, blocks nothing
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com; np=reject"

; 2. safe enforcement - failing mail is set aside, not lost
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; np=reject"

; 3. full enforcement
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

; 4. a domain that sends no mail at all
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s"
example.com.        3600 IN TXT "v=spf1 -all"
example.com.        3600 IN MX   0 .

How to verify what you published

Look the record up rather than trusting the control panel, because the two most common publishing mistakes are both invisible in the form you typed it into: the record landing at _dmarc.example.com.example.com because the panel appended the zone to a name you had already qualified, and the value being split into two TXT strings that no longer reassemble the way you intended.

Then run it through the DMARC checker, which will tell you what each tag resolves to including the defaults you did not write, and whether an external reporting address is authorised. Reading back the effective policy, defaults included, catches the class of mistake where the record is valid and does something other than what you meant.

Finally, wait for the first aggregate reports. A record is correct when the reports show your legitimate mail passing in alignment — not when it parses.

What commonly goes wrong

  • Two DMARC records at _dmarc, which receivers treat as none at all. Usually caused by adding a new record rather than editing the existing one.
  • A record published at the apex instead of at _dmarc. It is syntactically fine and completely inert.
  • An external rua address with no authorisation record in the reporting domain, so reports silently never arrive.
  • Copying pct=100 from an old article. It is a removed tag, it does nothing useful, and it will block a BIMI logo.
  • sp=none inherited from a template, quietly exempting every subdomain from an otherwise enforcing policy.
  • Moving to p=reject without ever having read an aggregate report. The reports exist to tell you what will break; skipping them does not remove the risk, it just moves the discovery to after the mail has stopped.

Check your domain with the dmarc record generator