Email Authentication

DMARC Record Syntax Explained (With Real Examples)

Learn how DMARC record syntax works, including every DMARC tag, allowed values, real examples, DNS setup instructions, and common mistakes to avoid when configuring DMARC authentication.

DMARC Record Syntax Explained (With Real Examples)

The Thing Nobody Tells You About DMARC Records

A DMARC record is one line of text. One. And yet, it's responsible for more broken email setups than almost anything else in DNS — because that single line has to be exactly right, the tags have to mean exactly what you think they mean, and some of the defaults are genuinely counterintuitive.

This guide is not a tag glossary with a table and a "good luck." It walks through every tag with real records, real tradeoffs, and the actual mistakes that get people in trouble. Start here if you are building a new DMARC record, or if you published one and something is not behaving the way you expected.

Before you build anything: Check whether a DMARC record already exists on your domain with the DMARC Lookup Tool. If you publish a second record without removing the first, you will get a permerror — and your DMARC policy will be completely ignored, silently, until someone thinks to check.


What a DMARC Record Actually Is

It is a DNS TXT record. You publish it at a very specific hostname — _dmarc. followed by your domain — and that underscore prefix is not optional. The record value is a semicolon-separated list of tag=value pairs.

A full, real-world record looks like this:

code
v=DMARC1; p=quarantine; sp=reject; pct=50; rua=mailto:dmarc@acme.com; ruf=mailto:failures@acme.com; fo=1; adkim=r; aspf=r

Every tag has a name, an = sign, and a value. Semicolons separate them. Order does not matter — except for v=DMARC1, which must come first, because that is how mail servers identify this TXT record as a DMARC record and not something else.

Now let's go through each tag and talk about what it actually does — and where it will bite you.


v= — Version

Required. Must be first. Only valid value: DMARC1.

code
v=DMARC1

There is no DMARC2. There is no other valid value. This tag exists purely so that mail servers can scan TXT records and identify which one is the DMARC record without reading everything else. If v=DMARC1 is not the very first thing in the record, the whole record gets ignored.


p= — Your Main Policy

Required. The tag that actually does something.

code
p=none      ← monitor only, no action taken
p=quarantine ← send failing mail to spam
p=reject    ← block failing mail entirely

This is the single most consequential tag. It tells every receiving mail server on the internet what to do when an email claims to come from your domain but fails DMARC checks.

Here is the honest breakdown:

p=none is not "off." It is "watching." Email still gets delivered exactly as normal, but participating mail servers send you daily XML reports about what they saw. This is where every new setup should start — you need those reports before you can safely enforce anything.

p=quarantine puts failing mail into spam rather than the inbox. This is softer than reject but it still affects real email. Do not jump here until your reports show that the only failing sources are actual spammers, not legitimate tools you forgot to configure.

p=reject is the goal, but it is not the starting point. A domain that rushes to p=reject without first understanding its sending landscape — third-party tools, internal systems, newsletter platforms — will start blocking its own emails. This happens more than people expect.

Three real-world records showing the progression:

code
# Week 1: you have no idea who is sending email as your domain
v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com

# After 3-4 weeks of reports: you've fixed your known senders
v=DMARC1; p=quarantine; pct=20; rua=mailto:dmarc@yourdomain.com

# Months later: everything legit is passing, crank it up
v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com

sp= — Subdomain Policy

Optional. Default: inherits your p= value.

code
sp=none | sp=quarantine | sp=reject

This is the most commonly misunderstood optional tag. When you publish a DMARC record for example.com, the policy you set with p= also applies to subdomains — mail.example.com, shop.example.com, and so on — unless you explicitly override that with sp=.

Why does this matter? Two scenarios come up regularly:

Scenario A: Your root domain is at p=reject but you have a staging subdomain (dev.example.com) that sends test emails with no DKIM configured. Without sp=, those test emails get rejected too.

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

This rejects failing mail from the root domain but just monitors subdomain failures. That buys you time to properly configure subdomains before enforcing.

Scenario B: You have a parked domain — something you own but do not actually send email from. You want maximum protection with no exceptions.

code
v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc@example.com

One genuine gotcha: if you set p=none and forget to set sp=, your subdomains are also on none — meaning attackers can spoof billing.example.com or support.example.com freely. Those subdomain names are often more convincing for phishing than the root domain itself.


pct= — Percentage of Mail the Policy Applies To

Optional. Default: 100.

code
pct=10    ← only 10% of failing mail gets the p= treatment
pct=100   ← all failing mail gets the p= treatment (the default)

pct= is a rollout tool. When you are moving from p=none to p=quarantine or p=reject and you are not 100% confident everything legitimate is configured correctly, pct= lets you ease in. Set it to 10 and only 10% of failing messages get quarantined. The other 90% still land in the inbox. Watch your reports. If your own legitimate mail starts ending up in spam, you know something is still misconfigured.

The thing people often do not realise: the selection of which 10% is random from the receiver's perspective. You cannot predict which messages will be affected, so this is not a reliable way to test with specific senders. It's a statistical rollout mechanism, not a staging environment.

Important: pct= has absolutely no effect when your policy is p=none. None means none — there is nothing to apply a percentage to.


rua= — Where Aggregate Reports Get Sent

Optional, but you should almost always include it.

code
rua=mailto:dmarc@yourdomain.com

This tells every participating mail server to send you a daily XML report summarising DMARC results for emails claiming to be from your domain. These reports are how you find out which services are failing, which IP addresses are spoofing you, and whether your legitimate mail is passing.

Without rua=, you are flying blind. You can publish a p=none record and feel like you have DMARC set up — but you have no data to act on, so it just sits there indefinitely doing nothing useful.

A few practical things worth knowing:

You can send reports to multiple addresses by separating them with commas, no spaces:

code
rua=mailto:dmarc@yourdomain.com,mailto:reports@thirdpartyanalyzer.com

The reports are XML files, often gzipped. They are not human-readable without a parser or a tool. If you want to monitor them, pipe them to a service or at minimum set up a dedicated mailbox so they do not clutter your main inbox.

Not all providers send reports. Most major ones do — Gmail, Microsoft, Yahoo — but smaller mail servers often do not bother implementing the reporting side. Your aggregate data is useful but it is not a complete picture of all email being sent as your domain.


ruf= — Where Forensic (Failure) Reports Get Sent

Optional. Often not worth adding.

code
ruf=mailto:failures@yourdomain.com

Forensic reports are per-message failure notifications. When a single email fails DMARC, you get a report for that specific message — including headers and sometimes body content.

Sounds useful. In practice, there are two problems:

First, Gmail does not send forensic reports. Yahoo does not send them either. Microsoft's stance has been inconsistent. The providers that send them are a minority. So your ruf= address will receive reports from a fraction of failure events, not all of them.

Second, forensic reports can contain message content — which creates privacy and data handling implications you probably do not want to think about. Some regions have regulatory concerns here.

The practical recommendation: set a ruf= address if you have a specific incident investigation need. For normal operations, your rua= aggregate reports contain enough information to identify what is failing and why.

RUA (Aggregate) RUF (Forensic)
Frequency Daily digest Per failure event
What's in it Statistics, IPs, pass/fail counts Individual message headers
Who sends them Most major providers A minority of providers
Privacy concern Low Potentially high

fo= — Failure Reporting Options

Optional. Only relevant if you have set ruf=.

code
fo=0   ← report only if both SPF and DKIM fail (default)
fo=1   ← report if either SPF or DKIM fails
fo=d   ← report only if DKIM fails
fo=s   ← report only if SPF fails

fo= controls the trigger condition for forensic reports. The default (fo=0) is actually quite conservative — you only get a report if the message fails both SPF and DKIM. A message that passes DKIM but fails SPF (or vice versa) does not generate a forensic report at fo=0.

If you want more visibility, fo=1 generates a report whenever either check fails. This produces significantly more reports and is the better choice if you are actively debugging something specific.

You can combine values with colons: fo=1:d means "report if either fails, and specifically when DKIM fails."

In most production setups, fo=0 is fine. The aggregate reports (rua=) are more actionable for day-to-day monitoring. fo= is something you adjust during active troubleshooting, then leave alone.


adkim= — DKIM Alignment Mode

Optional. Default: r (relaxed). Do not change this unless you know exactly why.

code
adkim=r   ← relaxed (default)
adkim=s   ← strict

Alignment is the concept of matching the domain in the technical authentication against the domain your recipients actually see in the From field. adkim= controls how strictly that match has to be for DKIM.

Relaxed (adkim=r): The DKIM signature's d= domain just needs to share the same organisational domain as your From address. So d=mail.example.com passes alignment for From: you@example.com because they both belong to example.com.

Strict (adkim=s): The d= domain must match the From domain exactly. d=mail.example.com would fail alignment for From: you@example.com.

Strict sounds more secure, and it is — in theory. In practice it breaks a lot of legitimate setups. If you use any email service provider that signs with a subdomain of your domain, strict DKIM alignment will cause those messages to fail DMARC. Relaxed is the right default for almost everyone.

The only time to consider strict: you have complete control over all email signing infrastructure, sign everything with exactly your root domain, and have a specific threat model that requires it.


aspf= — SPF Alignment Mode

Optional. Default: r (relaxed). Same story as adkim=, same recommendation.

code
aspf=r   ← relaxed (default)
aspf=s   ← strict

For SPF, alignment means the envelope From domain (the technical return-path address, which recipients never see) must match the header From domain (the one they do see).

Relaxed: The envelope From just needs to share the organisational domain with the header From.

Strict: They must match exactly.

This is the one that catches people the most. Nearly every email service provider — Mailchimp, SendGrid, HubSpot, Postmark, Amazon SES — uses their own domain in the envelope From (for bounce handling). That means SPF alignment for those services almost always passes under relaxed mode but fails under strict mode.

If you set aspf=s while using any third-party sending platform, you will break those emails. The only way around it is if the platform supports a custom bounce/return-path domain — some do, some do not.

Keep aspf=r unless you have done a full audit of every sending source and confirmed they all use your exact domain in the envelope From.

Check your current SPF configuration: SPF Lookup Tool.
If you are seeing SPF errors, also read: SPF Permerror: What It Means and How to Fix It.


ri= — Report Interval

Optional. Default: 86400 (one day). Effectively ignored by most providers.

code
ri=86400

This tag tells providers how frequently (in seconds) you want aggregate reports. The spec allows you to request hourly reports with ri=3600 if you are actively monitoring something.

In reality, virtually every major provider sends reports once per day regardless of what you put here. Set it if you want, skip it if you want — it probably will not change what you receive.


Real DMARC Records for Real Situations

Instead of generic yourdomain.com placeholders, here are records built for specific, actual situations:

Brand-new domain, day one, no idea what is sending email as you:

code
v=DMARC1; p=none; rua=mailto:dmarc-inbox@acme.com

No enforcement, just reports. Set this up first and leave it for 2–4 weeks before touching anything else.

Established domain, legitimate senders configured, cautiously starting enforcement:

code
v=DMARC1; p=quarantine; pct=10; sp=none; rua=mailto:dmarc@acme.com; fo=1

10% quarantine with forensic reporting active. Subdomain policy is still on none because the subdomains were not audited yet.

SaaS company sending transactional email through SendGrid + newsletters through Mailchimp, both properly configured:

code
v=DMARC1; p=reject; sp=quarantine; pct=100; rua=mailto:dmarc@acme.com; adkim=r; aspf=r

Full reject on root domain. Subdomains (a staging environment sends some internal alerts) are on quarantine rather than reject. Both alignment modes stay relaxed because the third-party senders need it.

Domain that exists but never sends email (brand protection domain, old product domain, etc.):

code
v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s

No rua= because you do not need reports — nothing legitimate should ever be sending from this domain. Strict alignment because there are no legitimate senders to break. Maximum protection, minimum maintenance.

Domain mid-migration, old mail system still sending, new system being rolled out:

code
v=DMARC1; p=quarantine; pct=5; rua=mailto:dmarc@acme.com; ruf=mailto:dmarc-failures@acme.com; fo=1

Very low pct during the transition window. Both aggregate and forensic reports active for maximum visibility while infrastructure changes are happening.


Publishing Your DMARC Record

The record goes into DNS as a TXT record. The hostname is always _dmarc — that underscore is mandatory and that exact string is what mail servers look for.

Your Domain The DMARC Hostname to Use
example.com _dmarc.example.com
shop.example.com _dmarc.shop.example.com
example.co.uk _dmarc.example.co.uk

Note: if you want a single DMARC policy to cover subdomains, you publish it at the root domain level only. You do not need to create _dmarc records for every subdomain — the root domain record inherits down, and sp= lets you differentiate the behaviour.

Most DNS panels (Cloudflare, GoDaddy, Namecheap, cPanel) have you enter _dmarc in the Name/Host field and the full record string in the Value/Content field. TTL of 3600 (1 hour) is sensible — short enough to fix mistakes quickly, long enough to not hammer DNS resolvers.

After you publish: use the DMARC Lookup Tool to confirm it is live. Use the TXT Lookup Tool if you want to see the raw record exactly as DNS returns it.


Syntax Mistakes That Actually Happen

Not hypothetical mistakes — the ones that show up repeatedly when people debug DMARC.

Forgetting mailto: in the rua or ruf address

code
# This silently fails on many validators
v=DMARC1; p=none; rua=dmarc@example.com

# This is correct
v=DMARC1; p=none; rua=mailto:dmarc@example.com

The mailto: prefix is not optional. Some mail servers are forgiving and parse it anyway. Others are not. Do not rely on the forgiving ones.

Publishing a second DMARC record instead of editing the first

This is the most common and most damaging mistake. If two TXT records exist at _dmarc.yourdomain.com, the result is a permerror. Your policy is not applied. Your reports stop. Everything looks fine from your DNS panel because both records are technically "published." Always check for existing records before creating one: DMARC Lookup Tool.

Using p=monitor instead of p=none

monitor is not a valid value. The valid values are none, quarantine, and reject. A record with p=monitor will either be treated as invalid or interpreted unpredictably depending on the receiver's implementation.

Adding spaces inside tag values

code
# Invalid — the space breaks the rua value
v=DMARC1; p=none; rua=mailto: dmarc@example.com

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

Setting pct=10 and thinking you are "testing" specific senders

pct= is random sampling, not targeted testing. If you set pct=10 to "test" whether your Mailchimp emails still get through, you might not see any quarantined Mailchimp emails for days — or you might see a bunch. It's 10% of all failures, selected randomly by each receiver. Use it for gradual rollout, not for targeted testing.

Going straight to aspf=s because it sounds more secure

Every major email platform — SendGrid, Mailchimp, HubSpot, Mailgun — uses their own domain in the envelope From (the return-path) for bounce handling. Strict SPF alignment (aspf=s) requires the envelope From to match your header From exactly. It will break all of them unless you have specifically configured a custom bounce domain (most people have not). Keep aspf=r.


Before You Move On

If you are still building out your email authentication setup, check these in order:

  1. Confirm your SPF record is correct → SPF Lookup Tool
  2. Confirm your DKIM key is published → DKIM Lookup Tool
  3. Publish your DMARC record and verify it → DMARC Lookup Tool
  4. View all your DNS records together → DNS Records Checker

If DMARC is already publishing and you are seeing failures you did not expect, the next article in this series goes through every cause of a DMARC fail and exactly how to fix each one: DMARC Fail Explained: Causes, Examples & Fixes.

For background on how SPF and DKIM work — which you need to understand before tuning DMARC alignment — read:

New to all of this? Start with What Is DMARC? Complete Beginner's Guide (2026) first.


Frequently Asked Questions

What is the minimum valid DMARC record?
Technically, v=DMARC1; p=none; is valid. Practically, it is useless without rua= — you have monitoring mode with no way to receive the reports. Always include a rua= address.

Do I need to include all optional tags?
No. Most production records only use p=, sp=, pct=, and rua=. The alignment tags (adkim=, aspf=) default to relaxed, which is what most setups need. Tags like ri= are almost never worth including because providers ignore them anyway.

Can I use a third-party service's email address for rua=?
Yes — this is how DMARC report analysis services work. Some require you to also publish a verification TXT record on your domain (to prove you authorize that third party to receive your reports). Check the specific service's setup instructions.

How long until my DMARC record is live after publishing?
DNS propagation varies. In practice, most resolvers pick it up within a few minutes to a few hours. Full global propagation can take up to 48 hours in edge cases. Use the DMARC Lookup Tool to check — if it shows your record, it is live from that resolver's perspective.

My DMARC record looks correct but reports are not arriving. Why?
A few possibilities: the rua= email address has a typo, the receiving domain's provider does not send reports, or it has only been a few hours and the first report has not been generated yet (most are sent at the end of the UTC day). Wait 24–48 hours and check your spam folder before troubleshooting further.


Last updated: 2026 | Category: Email Authentication | Reading time: ~14 minutes