# DMARC record explained

A DMARC record is a single line of text you publish in your DNS, on the name `_dmarc` in front of your domain. It tells [receiving providers](https://dmarced.eu/en/learn/basics/concepts#receiving-provider) two things: what you'd like done with mail claiming to come from you that can't be verified, and where to send the reports that show you who's sending in your name.

Here's an example of a DMARC record, and where it goes in DNS:

| Name | Type | Content |
| --- | --- | --- |
| `_dmarc.example.com` | TXT | `"v=DMARC1; p=reject; rua=mailto:dmarc@example.com"` |

Everything inside the quotes is a list of DMARC tags separated by semicolons, and each one does a single job. `v` has to come first. After that, two tags are enough to run a real policy, and the rest fine-tune reporting, alignment, and how far the policy reaches. Here's the full set.

*Tags can appear in any order, with one exception: v has to come first.*

| Tag | Description | Example |
| --- | --- | --- |
| [`v`](#v-version) | **Required**. Marks the record as DMARC | `v=DMARC1` |
| [`p`](#p-policy) | **Required**. What to do with mail that fails | `p=none`, `p=quarantine`, `p=reject` |
| `t` | **Optional**. Whether a testing policy is applied | `t=n; pct=50` , `t=y; pct=0` |
| [`rua`](#rua-reporting-uris-for-aggregate-data) | **Recommended**. Where to send aggregate reports | `rua=mailto:dmarc@example.com` |
| [`ruf`](#ruf-reporting-uris-for-forensic-data) | **Optional**. Where to send per-message failure reports | `ruf=mailto:failure@example.com` |
| [`sp`](#sp-subdomain-policy) | **Optional**. Policy for existing subdomains | `sp=reject` |
| [`np`](#np-non-existent-domain-policy) | **Optional**. Policy for subdomains that don't exist | `np=reject` |
| [`aspf`](#aspf-spf-alignment-mode) | **Optional**. How strictly the SPF domain must match | `aspf=s` |
| [`adkim`](#adkim-dkim-alignment-mode) | **Optional**. How strictly the DKIM domain must match | `adkim=s` |
| [`fo`](#fo-failure-reporting-options) | **Optional**. Which failures produce a failure report | `fo=1` |
| [`psd`](#psd-public-suffix-domain) | **Optional**. Whether this name is a public suffix domain | `psd=n` |

## Understanding what each DMARC tag does

Everything in a DMARC record is a request. Receiving providers that take part in DMARC read it the same way, but what happens to any individual message is always their decision. DMARC gives them one more thing to weigh, not an order to follow.

### 

### v: version

Marks the record as DMARC. It's required, it has to be the first tag, and `DMARC1` is the only valid value. The value is case sensitive, so `v=dmarc1` doesn't count.

Inbox providers ignore the entire record if this value isn't valid, or if the tag isn't the first. For any other tag, a wrong value will only cost you that tag, and the rest of the record still applies.

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

### p: policy

When a message arrives claiming to come from you, the receiving provider tries to prove it. If SPF or DKIM can vouch for the domain in your [header from](https://dmarced.eu/en/learn/basics/concepts#header-from) line, the message is yours and no policy is enforced.

`p` is for everything else. It takes `none`, `quarantine`, or `reject`, and it covers your subdomains too, unless `sp` or `np` say otherwise.

Here's what you're asking the receiving provider to do when a message can't be verified:

- `p=none` – nothing changes. It reaches the inbox, and you see it in your next DMARC report.
- `p=quarantine` – treat it as suspicious, which usually means the spam folder.
- `p=reject` – refuse it outright, so nobody sees it and the sender gets a bounce.

The catch: DMARC can't tell a spoofed message from your own invoicing tool running on a service nobody authorized. Both fail, and `p` treats them the same. That's why you start at `none` and read reports before tightening the policy further. 

Under the [original DMARC specification](https://www.rfc-editor.org/info/rfc7489/), `p` was required. Leaving it out would make the record not valid. [RFC 9989](https://www.rfc-editor.org/info/rfc9989/) relaxed that: a record without `p` is now read as `p=none` rather than discarded. Most receiving providers haven't adopted the new standard yet, so we'd always include the policy tag.

### sp: subdomain policy

Your `p` policy already covers subdomains. `sp` is there for when you want them treated differently.

That comes up more often than it sounds. Say `example.com` still sends through a few [sending providers](https://dmarced.eu/en/learn/basics/concepts#sending-provider) you haven't verified yet, so it sits at `p=none`. But usually nothing legitimate ever comes from `mail.example.com` or `news.example.com`, so anything claiming to be from them is spoofed. `sp=reject` asks receiving providers to refuse it, and your main domain stays untouched.

It takes the same three values as `p`: `none`, `quarantine`, or `reject`.

One catch trips almost everyone. `sp` only works on your organizational domain, the one you actually registered. Publish it on `mail.example.com` and receiving providers skip it. The record that governs your subdomains is the one at `example.com`.

Subdomains that don't exist in DNS have their own tag to enforce a policy, `np`.

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

### np: non-existent domain policy

Phishing campaigns like to invent subdomains. Names like `billing.example.com` or `secure-login.example.com` look official enough in the Header From, the address people see in their inbox, and they have never existed in your DNS.

Without `np`, those names follow whatever `sp` or `p` says. So while your domain sits at `p=none`, invented subdomains sail through with everything else.

`np=reject` closes that gap. It takes the same values as `p`, and it's unusually safe to set straight away: a name that doesn't exist can't be sending real mail, so there's nothing to break.

Like `sp`, it only works on your organizational domain.

```plain
v=DMARC1; p=none; np=reject; rua=mailto:dmarc@example.com
```

### t: testing

Moving to `p=reject` is the nervous part. The reports look clean, but you can't be certain until real mail starts getting refused.

`t=y` gives you a grace period. You publish the policy you intend to run, and receiving providers go one step easier on failing mail while you watch: `reject` is treated as `quarantine`, `quarantine` as `none`. Reports keep arriving exactly as before.

`t=n` is the default and means the policy applies as written. The tag does nothing at all when your policy is already `none`.

Take `t=y` back out once a week of reports shows nothing surprising.

This tag replaced `pct`, where `t=y` is what people used to write as `pct=0`, and `t=n` is `pct=100`. Adoption rate is split, so publish both. Receiving providers still on the original specification ignore `t` as an unknown tag and act on `pct`, while those on RFC 9989 do the reverse.

```plain
v=DMARC1; p=reject; t=y; pct=0; rua=mailto:dmarc@example.com
```

Take `t=y` and `pct=0` back out once a week of reports shows nothing surprising.

### rua: reporting URI(s) for aggregate data

This is the tag that gives you visibility. Without it you're publishing a policy blind: `p=reject` with no `rua` blocks mail and never tells you whose.

It lists the addresses where receiving providers send their daily reports on mail claiming to be from you, written as `mailto:` and separated with commas if you want several.

```plain
v=DMARC1; p=none; rua=mailto:dmarc@example.com
v=DMARC1; p=none; rua=mailto:dmarc@example.com,mailto:reports@vendor.example
```

Leave the tag out and no reports are sent at all.

```plain
example.com._report._dmarc.vendor.example    TXT    "v=DMARC1;"
```

Some vendors set this up for you, but miss the step and the reports quietly never arrive.

Aggregate reports are the ones that actually arrive. Google, Microsoft, Yahoo and most others send them reliably, and they're the only view you get of your own mail: how much, from where, what passed, what didn't. That's also what makes a policy safe to tighten.

One catch if the address sits on a domain you don't own. That domain has to agree to receive your reports by publishing a TXT record of its own:

They're also what makes a policy safe to tighten. `p=reject` with no `rua` blocks mail and never tells you whose.

Those reports arrive as XML, built for machines rather than people.

That's the part we handle: [dmarced](https://dmarced.eu/en/pricing) collects them for you and puts every source sending in your name on a dashboard, so you can see where your mail comes from and what passed or failed. A weekly summary lands in your inbox with what changed and what to fix. If you'd rather read the raw reports yourself, we've written up a learn page for [what's inside a DMARC XML report](https://dmarced.eu/en/learn/basics/dmarc/xml-report).

### ruf: reporting URI(s) for failure data

Where receiving providers send reports about individual messages that failed, rather than the daily totals you get from `rua`. The `fo` tag decides which failures qualify.

```plain
v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; ruf=mailto:failures@example.com; fo=1
```

Most receiving providers don't send these at all, Google and Microsoft among them, so a busy domain can go weeks with nothing arriving. Privacy is the reason: a failure report can carry headers and parts of the original message, which means real mail from real people landing in the mailbox you named here. That makes it a data protection decision as much as a technical one, and under GDPR it's worth a conversation before you publish the tag.

External addresses need the same `_report._dmarc` authorization as `rua`.

### fo: failure reporting options

Decides which failures earn a `ruf` report. It does nothing on its own, so if there's no `ruf` in your record, this tag is ignored.

- `fo=0` reports only when both SPF and DKIM fail to produce an aligned pass. This is the default.
- `fo=1` reports when either one fails, even if the other passed.
- `fo=d` reports when the DKIM signature itself didn't verify.
- `fo=s` reports when SPF evaluation failed.

You can combines these options with colons.

```plain
v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; ruf=mailto:failures@example.com; fo=1:d:s
```

The default is narrower than most people expect. A message where DKIM passed but SPF broke never gets reported, because DMARC as a whole still passed. That's usually the exact case you wanted to see. `fo=1` is the setting most people actually mean.

### aspf: SPF alignment mode

SPF checks the [envelope from](https://dmarced.eu/en/learn/basics/concepts#envelope-from) domain, which is the technical address mail servers use for routing and bounces. DMARC cares about the header from, the address your reader actually sees. `aspf` decides how closely those two have to match for an SPF pass to count.

`r` is relaxed and the default. Anything on the same organizational domain lines up, so a header from of [`news@example.com`](mailto:news@example.com) and an Envelope from of [`bounces@mail.example.com`](mailto:bounces@mail.example.com) align fine.

`s` is strict and demands the exact same domain. That same pair now fails alignment, even though SPF itself passed.

```plain
v=DMARC1; p=reject; aspf=s; rua=mailto:dmarc@example.com
```

Relaxed is what almost everyone wants. Sending providers routinely bounce mail through a subdomain of yours, and strict mode cuts all of that off. Where strict earns its place is a locked-down domain with one sender and no subdomains, where you want the narrowest possible definition of "yours."

Worth knowing either way: a sending provider that uses its own domain for bounces never aligns under either mode. SPF passes, DMARC ignores it, and DKIM has to carry the message on its own.

### adkim: DKIM alignment mode

Decides how closely the signing domain in a DKIM signature, the `d=` value, has to match the header from domain your readers see. `r` is relaxed and the default, `s` is strict.

Relaxed lets subdomains count, so a newsletter signed by `news.example.com` still aligns with a From line of `example.com`.

Relaxed lets subdomains count. A newsletter signed by `news.example.com` still aligns with `example.com`. Set `adkim=s` and it doesn't – the signature is perfectly valid, the service is legitimate, and the message fails DMARC anyway.

```plain
v=DMARC1; p=reject; adkim=s; rua=mailto:dmarc@example.com
```

Set `adkim=s` and that same newsletter fails. The signature is perfectly valid, the service is legitimate, and the message fails DMARC anyway, because the domains aren't identical.

Strict doesn't make you harder to spoof. Someone who can't produce a valid signature at all was already failing. What it rules out is your own infrastructure, so set it only when you know every sender signs with the exact domain in your From line.

### psd: public suffix domain ( advanced)

Marks whether this name hands out domains to other organizations. `y` says it does, `n` says it doesn't, and `u` is the default.

Before DMARC can check anything, it has to work out which domain here is yours. Receiving providers walk up the name one label at a time, `shop.example.co.uk`, then `example.co.uk`, then `co.uk`, and they need to know where to stop. You own `example.co.uk`. You don't own `co.uk`, and you'd rather a registry's settings weren't applied to your mail.

Registries publish `psd=y` to mark themselves, and the spec requires it of them.

```plain
v=DMARC1; p=reject; psd=n; rua=mailto:dmarc@example.co.uk
```

You almost certainly don't need this tag. Two cases make it worth knowing. The defensive one: if a registry above you publishes a DMARC record without marking itself properly, `psd=n` stops its settings being read as yours. The practical one: inside a large organization where DNS is split across departments, `psd=n` lets a department declare its own branch as the organizational domain, so it can run its own policy and collect its own reports instead of inheriting the ones at the apex.

## Deprecated DMARC tags

[RFC 9989](https://www.rfc-editor.org/info/rfc9989/) retired three tags. They still show up in older records, and inbox providers won't act on them. If you find one, you can take it out.

| Tag | What it did | Replaced by |
| --- | --- | --- |
| `pct` | Applied the policy to a percentage of mail | `t` |
| `rf` | Set the format for failure reports | Nothing, as only one format ever existed |
| `ri` | Asked for reports at a chosen interval | Nothing, as receivers set their own interval |

`pct` let you roll a policy out gradually – `pct=50` meant half your failing mail got the treatment. In practice only the extremes were ever used, so the tag became `t`. If your record says `pct=0`, swap it for `t=y`. Any other value can simply go.

`rf` named the format for failure reports, and `afrf` was the only format ever defined. The tag never decided anything.

`ri` asked for reports every so many seconds, usually `86400`. Inbox providers send aggregate reports on their own schedule – at least daily, sometimes more often – and have always ignored it.

## Resources

- [DMARC standard (RFC 9989, 2026)](https://datatracker.ietf.org/doc/html/rfc9989)