DMARC Engine
Home/Blog/The DMARC record explained, tag by tag
Blog

The DMARC record explained, tag by tag

A plain-English reference to every tag in a DMARC record: v, p, sp, rua, ruf, pct, adkim, aspf, fo, rf and ri. What each one does, the exact syntax it expects and the sensible default to reach for.

4 June 2026 · 15 min read

The DMARC record explained, tag by tag

A DMARC record is one line of text published in your DNS, and yet that single line decides whether the rest of the world will accept or reject mail claiming to come from your domain. It is deceptively short. A working record can be as compact as v=DMARC1; p=none; rua=mailto:reports@example.com, and a hardened one rarely runs past a few hundred characters. But every tag inside it carries weight, and a misplaced semicolon or a misunderstood default can quietly undo months of work.

This guide walks through every tag you can put in a DMARC record, one at a time: what it means, the exact syntax it expects, the values it accepts, and the sensible default to reach for. By the end you should be able to read any DMARC record at a glance and know precisely what it is telling receiving mail servers to do.

If you just want to inspect a live record without learning the grammar first, paste your domain into the DMARC checker and it will parse and explain every tag for you. If you would rather build one from scratch, the DMARC record generator produces valid syntax from a few choices. This article is the reference that sits behind both.

What a DMARC record actually is

DMARC stands for Domain-based Message Authentication, Reporting and Conformance. The record itself is a single DNS TXT entry published at a fixed, predictable hostname: the label _dmarc prefixed to your domain. For example.com, the record lives at _dmarc.example.com. For a subdomain like mail.example.com, it would live at _dmarc.mail.example.com, though as you will see, subdomains usually inherit policy from the parent.

The value of that TXT record is a list of tag-value pairs separated by semicolons. Each pair takes the form tag=value. Whitespace around the semicolons is tolerated by parsers, so both of these are valid and identical in meaning:

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

Tags are case-insensitive in their names, but some values are not. The mechanism keywords (none, quarantine, reject, r, s) are conventionally lower-case and you should keep them that way. The v=DMARC1 token is the one true exception to free ordering: it must come first, exactly as written, or receivers will treat the record as invalid and ignore it entirely.

A crucial point that trips people up: DMARC does not authenticate anything on its own. It is a policy layer that sits on top of two underlying checks, SPF and DKIM. DMARC asks whether either of those passed and whether the domain they passed for lines up with the domain in the visible From: address. That last part is called alignment, and several DMARC tags exist purely to tune how strict that alignment must be. If the concepts of SPF and DKIM are hazy, the SPF checker and DKIM checker are the quickest way to see them working on a real domain, and the glossary defines the terms used throughout.

v: the version tag (required, always first)

v=DMARC1

The v tag declares the DMARC version. Today there is exactly one valid value: DMARC1. This tag is mandatory, it must be the first tag in the record, and it must be spelled precisely as DMARC1 with no lower-case variation accepted in practice.

Its job is more important than it looks. Receivers locate your DMARC record by querying _dmarc.yourdomain and then scanning the returned TXT records for one that begins with v=DMARC1. If a domain has several TXT records at that name (which it never should for DMARC), or if the version token is malformed, the record is discarded as though it were not there. A surprising number of broken DMARC deployments come down to a stray second record or a typo in this opening tag. There is no sensible alternative value: it is always v=DMARC1, full stop.

p: the policy tag (required)

p=none
p=quarantine
p=reject

The p tag is the heart of DMARC. It tells receiving servers what to do with a message that claims to be from your domain but fails the DMARC check (meaning neither SPF nor DKIM passed in an aligned way). It accepts three values:

  • none asks the receiver to take no special action. Failing mail is still delivered as normal. This is monitor-only mode: you collect reports and learn who is sending as you, but nothing is blocked.
  • quarantine asks the receiver to treat failing mail as suspicious, typically routing it to the spam or junk folder rather than the inbox.
  • reject asks the receiver to refuse failing mail outright at the SMTP layer, so it never reaches the recipient at all.

p is required: a DMARC record without it is invalid. The sensible default when you first publish is p=none, because it lets you observe traffic without risking legitimate mail. But none is a starting line, not a finish line. A record left at p=none forever protects nobody, since it never tells receivers to stop a spoofed message. The whole point of DMARC is to move in stages from none to quarantine to reject once your reports confirm that every legitimate source is aligned. We cover why standing still is dangerous in Why p=none gives a false sense of security, and the staged rollout itself is what the done-for-you DMARC service automates.

sp: the subdomain policy tag (optional)

sp=reject

The sp tag sets a separate policy for subdomains of your domain. If you publish DMARC on example.com with p=quarantine and sp=reject, then mail failing DMARC from example.com itself is quarantined, while failing mail from anything.example.com is rejected.

When sp is absent, subdomains inherit the value of p. That inheritance is the part to remember, because it has real security consequences. Attackers love subdomains precisely because organisations forget about them: accounts.example.com or news.example.com may have no mail flow at all, yet without an explicit policy they are only as protected as the parent record makes them.

The sensible approach is usually to leave sp unset early on so subdomains inherit your cautious parent policy, then, once you reach enforcement, consider setting sp=reject to slam the door on unused subdomains even if the organisational policy is still quarantine. There is real nuance to how subdomains and DMARC interact, especially around whether a subdomain needs its own record at all, which we unpack in Does every subdomain need its own DMARC record?.

rua=mailto:dmarc@example.com
rua=mailto:dmarc@example.com,mailto:reports@thirdparty.com

The rua tag tells receivers where to send aggregate reports. These are the daily XML summaries that mailbox providers generate, one per sending source, listing how many messages they saw from each IP address and whether those messages passed SPF, DKIM and DMARC alignment. Aggregate reports are the single most valuable thing DMARC gives you, because they reveal every system sending mail as your domain, both the legitimate ones you forgot about and the spoofers you never knew existed.

The value is one or more mailto: URIs separated by commas. You can optionally cap report size with a suffix, for example rua=mailto:dmarc@example.com!10m requests reports no larger than ten megabytes, though receivers are free to ignore the limit. If the reporting address is on a different domain from the one being reported on, that other domain needs an authorisation record, but for same-domain reporting you can ignore that detail.

There is no formal default, but the practical default is: always include rua. A DMARC record without it is technically valid yet operationally blind, because you get no visibility into your own mail. Those XML files are dense and not meant for human eyes, so feed them into the DMARC report analyser to turn them into a readable picture, or let continuous monitoring parse them for you and alert on changes. For a guided tour of what the raw data contains, read Reading your first DMARC aggregate report.

ruf: forensic report address (optional, use with care)

ruf=mailto:forensics@example.com

The ruf tag requests forensic reports, also called failure reports. Unlike aggregates, which are statistical summaries, a forensic report is generated per failing message and contains a redacted copy of the message itself: headers, and sometimes parts of the body, for an individual email that failed DMARC. In principle this lets you inspect exactly what a spoofed message looked like.

In practice, ruf is far less useful than it sounds, for two reasons. First, the major mailbox providers largely stopped sending forensic reports years ago over privacy concerns, since the reports can contain personal data from real recipients. So even with ruf configured you may receive almost nothing. Second, what you do receive can be a firehose during an active spoofing campaign, landing real message content in your inbox.

The syntax mirrors rua: one or more mailto: URIs, comma-separated. A companion tag, fo, controls when forensic reports are generated (covered below). The sensible default is to leave ruf out unless you have a specific investigative need and a mailbox prepared to handle sensitive content. Aggregate reporting via rua does the heavy lifting for nearly everyone.

pct: the percentage tag (optional)

pct=25

The pct tag tells receivers what percentage of failing messages to apply your policy to. The value is an integer from 0 to 100. With p=quarantine; pct=25, a receiver applies quarantine to roughly a quarter of failing messages and treats the rest as p=none. It is a sampling dial, historically used to ease into enforcement so that any mistake in your configuration only affects a slice of mail rather than all of it.

The default when pct is absent is 100, meaning the policy applies to everything. That is also the value you want at the end of any rollout. Lower values can be useful as a brief confidence check when first moving from none to quarantine, but they should be temporary. Sitting permanently at pct=10 means ninety percent of spoofed mail still gets a free pass, which defeats the purpose.

One caveat worth knowing: pct interacts oddly with quarantine at some receivers, where the unsampled portion may be downgraded rather than simply delivered. Treat pct as a short-lived training wheel, ramp it to 100, and rely on your reports to tell you when it is safe to do so. Note too that the newer DMARCbis revision of the standard is deprecating pct in favour of a t= testing flag, so over time you will see it less, but it remains valid and widely honoured today.

adkim: DKIM alignment mode (optional)

adkim=r
adkim=s

The adkim tag controls how strictly DKIM alignment is checked. DKIM signs a message and ties the signature to a domain (the d= value in the DKIM signature). For DMARC to count that signature as a pass, the signing domain has to align with the From: domain. The adkim tag decides what "align" means:

  • r is relaxed mode (the default). The signing domain only needs to share the same organisational domain as the From: address. A signature from mail.example.com aligns with a From: of example.com.
  • s is strict mode. The signing domain must match the From: domain exactly. A signature from mail.example.com would not align with example.com under strict mode.

The default is r, and relaxed is the right choice for almost everyone, because it accommodates the normal reality of subdomains and sending platforms while still preventing cross-organisation spoofing. Reach for adkim=s only when you have a specific reason to demand exact-domain signatures and you are confident every legitimate signer uses the precise From: domain.

aspf: SPF alignment mode (optional)

aspf=r
aspf=s

The aspf tag is the SPF counterpart of adkim. It controls how strictly SPF alignment is checked. SPF validates the envelope sender (the MAIL FROM or Return-Path domain), and DMARC requires that domain to align with the visible From: domain. The values mirror DKIM:

  • r is relaxed (the default): the Return-Path domain and the From: domain need only share an organisational domain.
  • s is strict: they must match exactly.

The default is r, and again relaxed suits most senders. This matters more than it might seem, because many email service providers set a Return-Path on their own subdomain (for bounce handling) while sending From: your domain. Under relaxed SPF that still aligns; under strict it would not, and you could break SPF alignment for legitimate mail without realising it. Use aspf=s sparingly. If you are wrestling with SPF alignment at all, Why your service is not aligned at p=reject walks through the most common causes.

fo: forensic reporting options (optional)

fo=1

The fo tag fine-tunes when a receiver should generate the failure reports requested by ruf. It only has an effect if you have also published a ruf address. It accepts these values:

  • 0 (the default): generate a report only if both SPF and DKIM fail to produce an aligned pass.
  • 1: generate a report if either SPF or DKIM fails to produce an aligned pass. This is the most useful setting, because it catches cases where one mechanism passed and the other did not.
  • d: generate a report whenever DKIM fails to verify, regardless of alignment.
  • s: generate a report whenever SPF fails, regardless of alignment.

You can combine d and s with a colon, as in fo=d:s. The sensible default, if you collect forensic reports at all, is fo=1, since the standard default of 0 is needlessly narrow. But remember the earlier caveat: most providers no longer send these reports, so fo often has no observable effect.

rf: report format (optional, rarely changed)

rf=afrf

The rf tag specifies the format used for forensic (failure) reports. The only value in real-world use is afrf, the Authentication Failure Reporting Format, which is also the default. You will almost never set this tag explicitly, because there is no other format to choose. It exists for completeness and forward compatibility. If you see rf=afrf in a record, it is doing nothing the default would not already do. Leave it out.

ri: reporting interval (optional)

ri=86400

The ri tag requests how often, in seconds, you would like aggregate reports. The default is 86400, which is 86,400 seconds, or once per day. You can request a different interval, but this tag is best understood as a polite suggestion rather than a command. Receivers are explicitly permitted to ignore it, and in practice the overwhelming majority send aggregate reports once every twenty-four hours regardless of what you ask for. There is little reason to set ri at all. The daily default is what you will get and what you want.

Putting the tags together

Tags are independent, but they tell a story when read in sequence. Here is a record at the start of a deployment, monitoring only and gathering data:

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

Here is the same domain partway through rollout, beginning to quarantine a sample while still watching closely:

v=DMARC1; p=quarantine; pct=50; rua=mailto:dmarc@example.com; aspf=r; adkim=r

And here is a fully enforced record, the destination every domain should aim for, rejecting spoofed mail outright and protecting unused subdomains with an explicit subdomain policy:

v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc@example.com; aspf=r; adkim=r

Notice what is absent from the enforced record: no pct (because the default 100 is correct), no ruf or fo (because forensic reports add little), no rf or ri (because their defaults are fine). A clean DMARC record sets only the tags that change a default. Everything else is noise.

A quick reference to the defaults

When a tag is omitted, the receiver assumes a default. Knowing these saves you from publishing tags that do nothing:

  • v has no default; it is required and must be DMARC1.
  • p has no default; it is required.
  • sp defaults to whatever p is set to.
  • rua has no default; omitting it simply means no aggregate reports.
  • ruf has no default; omitting it means no forensic reports.
  • pct defaults to 100.
  • adkim defaults to r (relaxed).
  • aspf defaults to r (relaxed).
  • fo defaults to 0.
  • rf defaults to afrf.
  • ri defaults to 86400 (one day).

Common mistakes to avoid

A few errors come up again and again. The v=DMARC1 token must be first and exact; anything else invalidates the record. There must be only one DMARC record at _dmarc.yourdomain; publishing two is a frequent cause of silent failure. The p tag is mandatory, so a record that jumps straight to rua without a policy will be rejected by parsers. Reporting addresses must be valid mailto: URIs, and if they live on a different domain you need the matching authorisation record or those reports will never arrive. Finally, do not confuse strict and relaxed: setting aspf=s or adkim=s without understanding your senders is one of the surest ways to break alignment for legitimate mail. Run your draft through the DMARC checker before you publish, and it will flag most of these for you.

The practical takeaway

A DMARC record is a small contract written in DNS, and now you can read every clause of it. The version and policy tags do the load-bearing work, the alignment tags (adkim, aspf) decide how forgiving the checks are, and the reporting tags (rua above all) give you the visibility to move safely from monitoring to enforcement. Most of the remaining tags either repeat their own defaults or address forensic reporting that few providers still send, so a good record stays short and sets only what it needs to.

The hard part was never the syntax. It is knowing your record is safe to advance from p=none to p=reject without dropping a single legitimate message, and that judgement comes from reading your reports correctly over time. If you would like to see your current record parsed tag by tag, start with the free DMARC checker or browse the full set of diagnostic tools. And if you would rather have the whole journey to enforcement handled for you, with monitoring and change alerts built in, that is exactly what the hosted DMARC Engine service is built to do.

Share

See where your domain stands today

Run a free DMARC scan, then let us take you to enforced p=reject with no email outage.