← Back to Blog

DMARC subdomains: why a locked root domain can still be spoofed

  • Gmail
  • Admin console
  • Google Workspace
DMARC subdomains: why a locked root domain can still be spoofed

Say a 30-person distributor has done DMARC properly. SPF passes, DKIM signs every message, the record at the root domain sits at p=reject, and aggregate reports land in a mailbox every morning. Then customers start forwarding them fake invoices. The From address reads accounts@billing.example.com, a subdomain the company never created and does not use. The root domain is locked, and the forgery is happening one label to the left of it.

This is the most common hole we find on domains whose owners consider DMARC finished. The policy worked exactly as specified. It was simply never asked the question the owner assumed it was asked.

How a receiver finds the policy for a subdomain

DMARC does not evaluate the domain you configured. It evaluates the domain in the From header of the message in front of it, which the specification calls the Author Domain. Everything about subdomains follows from that one fact.

When a message arrives claiming to be from billing.example.com, the receiver queries _dmarc.billing.example.com first. If a valid record is there, that record decides the outcome and nothing at the root is consulted. If nothing is there, the receiver climbs upward to find the Organizational Domain, the name you actually registered, and applies the record it finds at that level.

The climb itself changed in May 2026, when DMARC became a Standards Track protocol as RFC 9989, obsoleting the informational RFC 7489 from 2015. RFC 7489 worked out the Organizational Domain from the Public Suffix List and queried exactly two names: the Author Domain, then the Organizational Domain. Intermediate levels were skipped entirely, so a record published at _dmarc.eu.example.com was invisible to a message from mail.eu.example.com. RFC 9989 replaces the list with a DNS tree walk, climbing one label at a time and bounded at eight lookups, until it finds the record that marks the boundary. If you have ever published a policy on a middle layer of a deep name and watched it do nothing at all, that is the behaviour that changed.

Either way, the practical rule is the same, and it is the one people get wrong: a subdomain with its own DMARC record ignores the parent completely. Publish v=DMARC1; p=none on a subdomain and it stays in monitoring no matter how strict the root domain is.

The sp tag, and the gap underneath it

When the record that ends up applying was found at the Organizational Domain rather than at the subdomain itself, the receiver does not use p. It looks for sp, the subdomain policy, and falls back to p only when sp is absent. That default is a safe one. A bare p=reject with no sp tag does protect every subdomain that has no record of its own.

The trouble starts because sp=none is genuinely useful during a rollout. It is how you enforce on the root while an invoicing system or a marketing platform on a subdomain is still being sorted out, and we recommend it for exactly that. It is also what plenty of copy-pasted records carry for no reason at all.

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

Under RFC 7489 that record was all or nothing. The sp value covered every subdomain, whether it was a real one you were nursing through a migration or a name an attacker invented five minutes ago. To keep one real subdomain in monitoring, you had to leave every imaginary subdomain in monitoring with it. That is the gap our hypothetical distributor fell into.

RFC 9989 adds np, a policy that applies only when the Author Domain does not exist. The specification ties existence to DNS itself: if a query for the name returns NXDOMAIN, then that name and any subdomain of it do not exist. Valid values are the familiar three, none, quarantine and reject, and np takes precedence over sp for any name that fails the existence test.

_dmarc.example.com.  TXT  "v=DMARC1; p=reject; sp=none; np=reject; rua=mailto:dmarc@example.com"

Read that record as a sentence: enforce on the root, hold the real subdomains in monitoring while we finish the work, and reject anything claiming to come from a name that was never created. Few domains have a good reason to be missing that third clause.

One distinction decides which tag covers what. Existence is a DNS question, not a mail question. A name like app.example.com with an A record pointing at a web application exists. It has never sent mail and never will, but it answers in DNS, so it falls under sp and not under np. Receiver support for np is also not universal yet, so treat it as a layer you add rather than as the only thing standing between you and a forged subdomain.

What we check first on a client domain

Before touching a single record we build the list, because every decision below depends on knowing which subdomains are real and which of them send.

  1. Every name under the domain that resolves, read from the DNS zone rather than from memory. Web apps, staging, VPN endpoints, a printer someone named years ago.
  2. Which of those actually send mail, taken from aggregate reports rather than from what anyone believes.
  3. Whether each sending subdomain has its own _dmarc record, and what it says. A forgotten p=none on a subdomain is a silent override of a strict root.
  4. Whether each sending subdomain has its own SPF record.

That last item is the asymmetry that surprises even experienced administrators. DMARC climbs the tree. SPF does not. SPF is evaluated against the exact domain in the envelope sender, and when there is no TXT record at that exact name the result is none. The record at the root is never consulted and does not help. So a billing system sending as billing.example.com with no SPF record published there fails SPF on every message, and the only thing keeping those invoices deliverable is an aligned DKIM signature. Enforce DMARC without checking that first and the thing you break is your own invoicing.

Subdomains that should never send

For every name on the list with no business sending mail, we publish three records. Order matters less than all three being present.

app.example.com.        MX   0 .
app.example.com.        TXT  "v=spf1 -all"
_dmarc.app.example.com. TXT  "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

The first is a null MX, defined in RFC 7505 as a single MX record with preference 0 and a zero length exchange written as a dot, announcing that the name accepts no mail at all. A domain advertising a null MX must not advertise any other MX record, so clear out anything stale before you add it. The second says no host is authorised to send as that name. The third stops the name depending on sp at all, which is what makes the pattern worth the effort on the handful of names that matter, even once np is in place.

In a large zone we cover only the names that look plausible in a forged From address: billing, invoices, pay, hr, payroll, secure, and the name of whatever finance system the company runs.

Delegating a subdomain to a marketing platform

The opposite case is a subdomain that exists precisely so somebody else can send from it. When a company runs campaigns through an email platform, moving that traffic to news.example.com instead of the root domain buys two things worth having.

The first is reputation separation. A campaign that collects complaints damages the reputation of the name that sent it, so keeping bulk mail off the name your quotes and invoices go out on means one bad send does not follow your sales team into the inbox.

The second is the SPF lookup budget. SPF allows ten DNS lookups during evaluation, and a record that exceeds it returns permerror, which fails SPF outright. Platform include mechanisms are the usual reason a domain runs out of room. Because SPF is evaluated per name, an include living on news.example.com costs the root domain nothing. Delegating your two or three heaviest senders to their own subdomains is often a cleaner fix than flattening records that then quietly go stale. We covered the counting and the alternatives in the guide to getting under the ten lookup limit.

On a delegated subdomain we set up the platform's SPF and DKIM records at that name, its own _dmarc record so the subdomain can sit at a different policy from the root while it is warming, and a check that the platform signs with a domain that aligns. Alignment is where these arrangements usually come apart. In the default relaxed mode, SPF and DKIM align when the authenticated domain and the From domain share the same Organizational Domain, so a DKIM signature carrying d=example.com aligns with a From address at news.example.com. In strict mode, set with adkim=s or aspf=s, the names must match exactly, and that same pairing fails. Turning on strict alignment without auditing subdomain senders first is a reliable way to break a campaign. The platform-specific records for the common senders are in our post on Mailchimp, HubSpot and Klaviyo alignment.

Reading the subdomain rows in your reports

Aggregate reports, which RFC 9990 specifies in its own document, are where you learn which subdomains exist in practice rather than on paper. Two habits make them useful for this.

Read the header From domain in each record block, not only the policy you published. Reports group by the domain the messages claimed, so subdomain traffic shows up as its own rows, easy to skim past when you are scanning for volume. A row for a subdomain you do not recognise is the entire reason to look.

Then check what the report says was actually applied. The published policy block gives you your record as the receiver read it, sp included, so you can confirm your subdomain policy is being seen the way you intended instead of assuming it from your own DNS. When a subdomain shows failures that were delivered anyway, the usual answer is a forgotten _dmarc record on that subdomain quietly overriding the root, and the report is where that becomes visible. Our walkthrough of the XML is in how to read a DMARC aggregate report, and you can paste one into our DMARC analyzer to see the rows decoded.

How Guanacos Tech helps

Subdomain work is inventory before it is DNS. We pull the zone, reconcile it against what the reports show is really sending, and then decide name by name which subdomains get a proper delegated sending setup, which get locked down, and which can simply inherit from the root. If your root domain is already at p=reject and you want to know whether that protection reaches one level down, run your record through the analyzer or book a call and we will walk your zone with you.

Sources

Frequently asked questions

Does every subdomain need its own DMARC record?

No. A subdomain with no record of its own inherits from the organizational domain, using the sp tag when it is present and p when it is not. Publish a record on the subdomain itself when you want it to run at a different policy from the root, for example a delegated sending subdomain that is still warming up.

My domain is at p=reject. Are my subdomains protected?

Usually yes, provided you have not published sp=none and no subdomain carries a weaker record of its own. Those two things are what quietly undo a strict root. Check your record for an sp tag, then check each sending subdomain for a forgotten _dmarc entry.

What is the difference between the sp and np tags?

The sp tag sets the policy for subdomains that exist in DNS. The np tag, added in RFC 9989, sets the policy for names that do not exist, meaning a lookup for them returns NXDOMAIN. For those names np takes precedence over sp, so you can hold a real subdomain in monitoring while still rejecting invented ones.