DMARC: what your record instructs a receiver to do
Your scan shows a DMARC record, so the box looks ticked. Read the record again. If it says v=DMARC1; p=none; rua=mailto:[email protected] then what you have published is an instruction to every receiving mail server on the internet to deliver forged mail from your domain exactly as it did before, and to send you a report about it afterwards. The policy lives in one tag, and until that tag says quarantine or reject, all you have is monitoring.
The record a receiver reads
A DMARC record is one TXT record published at _dmarc. in front of your domain. You can pull it the same way a receiver does:
dig +short TXT _dmarc.example.com
"v=DMARC1; p=none; rua=mailto:[email protected]"
A receiver that supports DMARC takes the domain from the From: header of an incoming message, does that lookup, and reads the tags left to right. Tags are separated by semicolons. Only the first tag has a fixed position. Any tag the receiver does not recognise is ignored, which is why a misspelt tag name produces no error: the receiver drops it and carries on with the defaults.
Then the receiver checks whether the message passed SPF or DKIM for a domain that aligns with the From: domain. If neither passed in alignment, the message has failed DMARC, and the record decides what happens next. Every tag below either sets that decision or shapes how it is applied.
v=DMARC1 goes first
v=DMARC1 must open the record, with that exact spelling and that exact case. A receiver treats V=dmarc1 or v=DMARC as no record at all. The same goes for a record where the version tag has slipped to second place behind p=.
There is a second way to have no policy while believing you do. If two TXT records at _dmarc.example.com both begin v=DMARC1, receivers are required to treat the domain as having no DMARC record at all. This happens when a second provider adds its own record during onboarding and nobody removes the first. The scan reports both cases as an invalid record rather than an absent one.
p= decides what happens to a failed message
p= takes three values, and each one is a specific instruction to the receiver about a message that failed.
p=none says deliver it as you normally would, and if you send reports, include it. Nothing about the message changes. A receiver records the failure in the Authentication-Results: header and hands the message to the inbox:
dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=example.com
The dis=NONE at the end is the disposition. It means the receiver saw the failure and did nothing with it, which is what your policy asked for.
p=quarantine says treat it as suspicious. In practice that means the junk folder, though the receiver is free to apply any handling short of refusal. The header changes to dis=QUARANTINE and the message still arrives at the mailbox, just somewhere the person is less likely to look.
p=reject says refuse it during the SMTP conversation. The message never lands anywhere on the receiving side, and the sending server gets a permanent failure that reads something like:
550 5.7.26 Unauthenticated email from example.com is not accepted due to domain's DMARC policy
That bounce is where the protection happens. A forger sending as your domain sees it and moves on. A legitimate system you forgot to authorise sees it too, which is why nobody sensible sets reject on day one.
Of the 316,600,902 graded and dead domains in the September 2026 census, 78,444,857 publish a DMARC record. 41,798,631 of those records say p=none. 36,602,897 are enforcing, meaning they say quarantine or reject. Each record in the first group tells receivers, in writing, to deliver the forgery.
Check which group yours is in. The scan reads the live record and shows you the p= value along with everything else on your domain.
sp= and the subdomains you forgot about
Subdomains inherit whatever p= says unless sp= overrides it, and inheriting is the behaviour you want. When it is present and weaker than p=, it opens a door you thought you had closed.
"v=DMARC1; p=reject; sp=none; rua=mailto:[email protected]"
That record refuses forged mail from example.com and delivers forged mail from invoices.example.com, hr.example.com and any other subdomain, whether or not the subdomain exists. A receiver does not check that a subdomain has its own MX or its own records. It takes the From: domain, finds no _dmarc. record there, walks up to the organisational domain, finds yours, and applies sp=.
The sp=NONE you saw in the header above is this tag being reported back to you. The reasons people set it, and the one case where a separate subdomain policy earns its place, are on the subdomain policy page.
rua= and the report that arrives every day
Put an address in rua= and each participating receiver sends it one aggregate report per day per sending domain, as a compressed XML file attached to an otherwise empty message. One row from one of those files looks like this:
<record>
<row>
<source_ip>203.0.113.7</source_ip>
<count>14</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
</record>
Fourteen messages from one address, claiming to be from you, failing both checks, delivered anyway because the disposition was none. Whether that address is your payroll provider or someone else’s botnet is something only you can tell, and the report is the only place you will ever see it. If the rua= address is a mailbox nobody opens, you may as well leave the tag out.
Reports only go to an address at a different domain if that domain publishes a record agreeing to receive them, at example.com._report._dmarc.otherdomain.example, so if a reporting service has not asked you to add that record, its reports will never arrive. A record with no rua= at all still enforces, though without reports you have no way of seeing what it is enforcing against.
ruf= and fo= for failure reports
ruf= names an address for failure reports, sent per message rather than per day, containing the headers of the failing message and sometimes its body. Very few arrive in practice, since receivers are wary of forwarding message contents, so the tag is close to decorative. fo= decides what triggers one: fo=0, the default, only when both SPF and DKIM fail in alignment; fo=1 when either does; fo=d on any DKIM failure and fo=s on any SPF failure. If your reporting tool asks for fo=1, set it and expect few.
pct=, adkim= and aspf= water the policy down
pct= is the percentage of failing messages the policy applies to, and it defaults to 100 when absent. p=reject; pct=10 means one failing message in ten is rejected and the other nine get the next policy down, which is quarantine. Under p=quarantine; pct=10 the other nine get none. It exists so you can ease into enforcement, and it becomes a problem when someone sets it during rollout and never takes it out. If a record has said pct=10 for two years, nine tenths of what it does is monitoring.
adkim= and aspf= set how tightly the authenticated domain has to match the From: domain. Both default to r, relaxed, where mail.example.com aligns with example.com. Setting s, strict, requires an exact match. Strict alignment is the right default for almost nobody, because most bulk and transactional platforms sign or send from a subdomain.
The failure you are more likely to see runs the other way: a platform passes SPF for its own envelope domain, which does not align with yours at all, so the pass counts for nothing. How the envelope domain and the From: domain drift apart, and what to put in your record so they line up, is on the SPF page.
The tags that were never read
A typo in a tag value is worse than a typo in a tag name. p=quarentine is a value the receiver cannot parse, and the specification is precise about what happens next: if the record has a valid rua=, the receiver treats the policy as p=none; if it does not, the receiver discards the entire record and the domain has no DMARC. The same rule applies to a record with no p= tag at all.
The census sees quarantine spelt more ways than you would credit, in more languages than you would expect, and behind every one of them sits an owner who believes they are enforcing. The typo page lists the ones the scanner catches and what each turns into.
Whitespace causes the same kind of trouble. p = none, with spaces around the equals sign, is accepted by some receivers and rejected by others. Write tags with no spaces except after the semicolon.
Moving p= from none to reject without bouncing your own invoices
Start by reading a month of aggregate reports and listing every source IP that sends as your domain. Each row that shows a pass for SPF or DKIM in alignment is a source you have already authorised. Each row that fails both is either a forger or a system of yours that nobody set up properly, and the source IP tells you which. The one that surprises people is usually a ticketing tool or a photocopier in a cupboard that sends from your domain and always has.
Fix the legitimate sources so they pass in alignment, either by adding the platform’s include to your SPF or by having it sign DKIM with your domain. Once the reports show no legitimate source failing, change p=none to p=quarantine. Give it a fortnight. The people whose mail lands in junk will tell you quickly.
When the reports show nothing new failing, change quarantine to reject. Leave pct= out entirely, so it stays at 100. Keep sp= out unless you have a subdomain with a real reason to differ. Keep rua= pointing somewhere a person or a tool reads, because the forgeries carry on after you enforce, as bounces you only ever see in the reports, and that is how you know it is working. Then run the scan again and read the p= line one more time.
Figures as of 5 September 2026, from the September 2026 edition of the defaults.exposed census. Census numbers move every month; the current values are on the census data page.