Defaults.Exposed

Do subdomains inherit your DMARC policy? The sp= tag nobody sets

Yes. Subdomains inherit your DMARC policy, and they inherit it whether or not they exist. When a receiver gets a message from invoices.example.com it looks for a record at _dmarc.invoices.example.com, finds nothing, walks up to _dmarc.example.com and applies what it finds there. One tag changes that answer. With v=DMARC1; p=reject; sp=none; in your record, the reject covers the apex only, and mail claiming to come from any subdomain, including ones you’ve never created, gets checked against none.

The lookup a receiver does before it reads your policy

A receiver takes the domain from the From: header (the envelope sender doesn’t come into it) and queries TXT at _dmarc. in front of it. If that returns a DMARC record, the search stops there. If it returns nothing, the receiver works out the organisational domain using the Public Suffix List, so invoices.example.co.uk resolves to example.co.uk rather than co.uk, and queries _dmarc. in front of that instead.

There is no third step. For mail from eu.mail.example.com the receiver tries _dmarc.eu.mail.example.com and then _dmarc.example.com, and skips _dmarc.mail.example.com entirely. You can run the same two queries yourself:

$ dig +short TXT _dmarc.invoices.example.com
$ dig +short TXT _dmarc.example.com
"v=DMARC1; p=reject; sp=none; rua=mailto:[email protected]"

The first query returns nothing, so the second record governs. At no point did the receiver look for an A record or an MX at invoices.example.com, or ask whether a mailbox exists there. Someone can put a name they invented five minutes ago in a From: header and the receiver runs the same two queries it would run for a subdomain you’ve used for years.

sp= is the only tag that changes the answer

Once the receiver is holding your organisational record because the subdomain had none, it reads sp= to decide what to do. The tag takes the same three values as p=: none, quarantine and reject. When it’s absent, subdomains get whatever p= says, which is inheritance working as designed.

A record with p=reject and no sp= therefore covers the apex and its subdomains alike, and needs nothing added. The trouble starts when sp= is present and weaker than p=. p=reject; sp=none puts two policies in one record, and it’s the weaker of the two that applies to names under your domain, of which there’s no fixed list.

The September 2026 census counts 78,444,857 DMARC records published across the graded and dead domains, and how many of those carry an explicit sp= isn’t a figure it reports. Leaving the tag out and inheriting is the right call. Owners who did set it usually had a reason that made sense at the time, and the reason has usually stopped being true while the tag hasn’t.

There’s a newer tag, np=, that sets a policy for subdomains with no A, AAAA or MX record in DNS, and it defaults to the value of sp=. Receivers that support it can treat billing.example.com differently depending on whether the name resolves. Support is partial, so don’t rely on it. The fix below works with receivers that have no idea what np= is.

The message that gets through

Someone sends an invoice to one of your customers. The From: header reads [email protected]. There’s no SPF record at billing.example.com, because the name doesn’t exist, and there’s no DKIM signature, because they don’t have your keys. Both checks fail. The receiver looks up _dmarc.billing.example.com, finds nothing, takes your organisational record and reads sp=none.

The receiving server writes this line into the message:

Authentication-Results: mx.google.com;
       dmarc=fail (p=REJECT sp=NONE dis=NONE) header.from=billing.example.com

Each part of that line is the receiver telling you what it did. The message failed (dmarc=fail), the receiver read your organisational policy (p=REJECT), then found a subdomain policy in the same record and used that instead (sp=NONE). dis=NONE is the disposition: delivered, no action taken, as your record instructed, so the customer now has a message from your domain in their inbox with a payment link in it.

Had the same message claimed to be from [email protected], the apex, the sending server would have been refused during the SMTP conversation with something like:

550 5.7.509 Access denied, sending domain example.com does not pass DMARC verification and has a DMARC policy of reject

Nobody sensible forges the apex of a domain at reject when a subdomain of the same domain is at none. The From: address billing.example.com looks, if anything, more plausible to a customer than the bare domain, since real companies send invoices from names like that.

Of the 78,444,857 DMARC records in the September 2026 census, 16,716,061 say p=reject. Those are the owners who did the hard part, moved off none and mostly stopped watching. If one of those records also carries sp=none, the owner has stopped watching a policy that still delivers forged mail from any subdomain.

How sp=none got into your record

Most of the time it was a staged rollout. The apex was ready for reject, and a marketing or newsletter subdomain was still sending through a platform nobody had aligned. Rather than hold the apex back, someone set sp=none to keep that subdomain delivering, meaning to come back to it. Later the platform was replaced and the subdomain went quiet, and the tag outlived both.

Sometimes a provider wrote it. Some onboarding wizards and setup guides generate a full record with all the tags spelt out, and sp=none arrives in the same paste as p=quarantine. The owner publishes what the wizard gave them, and the wizard was optimising for nothing breaking on day one.

There’s a worse variant than sp=none. sp=non and sp=quarentine are values a receiver can’t parse, and the specification is explicit about an unparseable sp=: if the record has a valid rua= address, the receiver acts as though the whole record said p=none, and if it doesn’t, the receiver applies no DMARC at all. A typo in the subdomain tag takes the apex down with it. The typo page lists the spellings the scanner catches and what each one turns into.

The one case where a subdomain earns its own policy

If a subdomain sends real mail you can’t yet get to pass in alignment, the fix is a record at that subdomain rather than a weaker sp= on the organisational one. Publish _dmarc.news.example.com with v=DMARC1; p=none; rua=mailto:[email protected] and the receiver stops at that record for mail from news.example.com and applies it. Your organisational record stays as it was, and the exception only reaches the one name you gave it.

A subdomain’s own record wins because the receiver never gets as far as walking up. That’s also why the apex can stay at reject with no sp= while news.example.com runs at none for as long as the platform needs. When the platform signs with a key published under your domain, the subdomain passes DKIM in alignment and the exception can go. How to get a platform signing with your domain rather than its own is on the DKIM page.

The tag also earns its place running the other way. If the apex is still at p=none while you work through a month of reports, and no subdomain of yours sends mail at all, then p=none; sp=reject closes the subdomains today without waiting for the apex. That’s the tag doing useful work, because it’s set stronger than p= rather than weaker.

The record to publish

Read your current record with the second dig query above and find sp=. If it isn’t there and p= says reject, you have nothing to change for subdomains. If it’s there and says the same as p=, delete it; it does nothing except give someone a tag to weaken later. If it’s there and weaker than p=, either delete it or set it to reject, then give the subdomains that send mail their own records as above.

Which leaves a record like this:

v=DMARC1; p=reject; rua=mailto:[email protected]

DNS changes take as long as your TTL to reach receivers. After that, test it the way an attacker would. From a mailbox at some other domain, send a message with the From: header set to a subdomain that doesn’t exist and watch what comes back. With inheritance working you’ll get a bounce carrying a 550. With sp=none still in place the message arrives, and the Authentication-Results: header shows sp=NONE dis=NONE to explain why.

The scan reads your live organisational record and shows you the sp= value, or its absence, alongside the rest of your domain.

Everything else in that record is on the DMARC page. Fix sp= first: it’s one tag and one DNS edit, and until then your reject protects the apex hostname and nothing under it.

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.