Your root domain is p=reject. Your subdomains can still betray you — here’s how

You did the work. SPF configured, DKIM keys published, DMARC policy at p=reject. Your DMARC reports show clean passes. You’re done.

Except three subdomain DMARC gaps can still hand attackers a working phishing address — and they enumerate them deliberately.

Phishing emails sent from your subdomains can still reach your customers’ inboxes through gaps that p=reject alone doesn’t close. RFC 7489 §6.3 says that when sp= is absent, subdomains inherit p= by default — so the root policy does apply. But three exploitable gaps remain, and attackers know exactly where to look.

This is the subdomain DMARC gap. It’s not obscure. Attackers enumerate it deliberately.

The three gaps your root DMARC policy leaves open

Gap 1 — A subdomain-specific record overrides the root. DMARC lookup checks _dmarc.billing.yourcompany.com before falling back to _dmarc.yourcompany.com. If a subdomain-specific record exists — left by a decommissioned SaaS integration, misconfigured by a vendor, or set during a forgotten pilot — it wins, regardless of what your root says. A p=none record on any subdomain is a clean override of your root p=reject.

The attack path via Gap 1:

  1. Attacker finds _dmarc.mail.yourcompany.com TXT "v=DMARC1; p=none" left from an old service
  2. Sends phishing email with From: mail.yourcompany.com
  3. Receiving server finds the subdomain record — policy is p=none
  4. Root fallback never applies — subdomain record takes precedence
  5. Email lands in the inbox

Gap 2 — Non-existent subdomains. RFC 9091 introduced the np= tag for NXDomain subdomains — addresses that resolve nowhere in DNS. Without np=reject, receiver behavior on a from-address like billing.yourcompany.com (no DNS record whatsoever) is implementation-dependent. Some receivers correctly apply the inherited p=; others don’t.

Gap 3 — Implicit policy relies on receiver compliance. A record without sp= depends on every receiver correctly implementing the RFC default. Most major ones do; edge cases and misconfigured receivers exist. sp=reject makes the intent explicit, audit-visible, and independent of receiver interpretation.

The sp= tag and what it does

RFC 7489 defines the sp= tag to specify the policy applied to subdomains of the organizational domain when no subdomain-specific DMARC record exists.

Your current DMARC record probably looks something like:

v=DMARC1; p=reject; rua=mailto:...

With sp= added:

v=DMARC1; p=reject; sp=reject; rua=mailto:...

That one addition tells receiving mail servers: if you’re evaluating mail from any subdomain of this domain and there’s no subdomain-specific record, apply reject. No exceptions.

The three values mirror the main p= tag: none (no action, monitor only), quarantine (spam folder), reject (block delivery).

Why sp=reject is the right default for most organizations

If your subdomains don’t send email — and most of them don’t — sp=reject is the correct setting. You’re not rejecting legitimate mail. You’re rejecting all mail, which is exactly right for subdomains that have no business sending email in the first place.

There is also a useful rollout pattern: set p=quarantine on the root while you’re still auditing your root-domain senders, but set sp=reject immediately for subdomains you know don’t send email. This lets you tighten subdomain policy ahead of root enforcement — explicit sp= is the only way to decouple them.

The domains that need consideration are the ones that legitimately send email from a subdomain:

  • mail.yourcompany.com — sometimes used as a return-path domain by sending services
  • em.yourcompany.com or sends.yourcompany.com — common SendGrid and Mailchimp tracking domains
  • helpdesk.yourcompany.com — Zendesk, Freshdesk, or similar

For these, you have two options. The cleaner approach is to publish a full DMARC record at _dmarc.mail.yourcompany.com with the appropriate policy for that specific subdomain. The subdomain-specific record takes precedence over the fallback, so it won’t inherit sp=reject from the root. The messier approach is to set sp=quarantine at the root — slightly softer than reject — to give yourself room for subdomains that aren’t cleanly configured.

The practical recommendation: set sp=reject at the root, then audit which subdomains actually send email and give them their own records.

How to find your sending subdomains

Before touching your DMARC record, you need to know which subdomains send email. There are three places to look.

Your sending service dashboards. SendGrid, Amazon SES, Postmark, and similar services show you the domains and subdomains they’re authorized to send from. Check the verified sender domain list in each service you use.

Your DNS records. Look for CNAME entries pointing to third-party sending infrastructure. em.yourcompany.com CNAME something related to SendGrid or Salesforce Marketing Cloud is a sign that subdomain sends email. Look for SPF records on subdomains too — if mail.yourcompany.com has its own v=spf1 record, it’s sending email.

Your DMARC aggregate reports. If you’re receiving aggregate report XML, look for records where the header_from shows a subdomain. If billing.yourcompany.com is appearing in reports with pass results, that subdomain is in use. If it’s appearing with no results, it might be a spoofing target.

DMARC Guardian’s domain overview shows subdomain activity alongside your root domain — if any of your subdomains have appeared in aggregate reports, they show up in the sources view.

The wildcard subdomain problem

Even with sp=reject in place, there’s a related attack vector worth understanding: wildcard subdomains that your DNS legitimately resolves.

If your DNS has a wildcard A record (*.yourcompany.com → some IP), every possible subdomain resolves. That doesn’t enable phishing directly — DMARC is about email authentication, not DNS resolution — but it does mean that any service you spin up under a subdomain will inherit sp=reject protection automatically, without you needing to remember to configure DMARC for it. Which is exactly the behavior you want.

What you’re protecting against with sp=reject is an attacker using a non-resolving subdomain (billing.yourcompany.com with no DNS record at all) in the From: header of a phishing email. Receiving mail servers don’t require the subdomain to resolve in DNS. They evaluate the DMARC policy based on the organizational domain alone. sp=reject tells them: reject that mail regardless.

The fix is one DNS change

If you’ve confirmed which subdomains legitimately send email and given them their own DMARC records, the root fix is straightforward:

_dmarc.yourcompany.com  TXT  "v=DMARC1; p=reject; sp=reject; rua=mailto:your-rua-address"

If you’re not sure yet and want to audit first, add sp=quarantine as an intermediate step:

_dmarc.yourcompany.com  TXT  "v=DMARC1; p=reject; sp=quarantine; rua=mailto:your-rua-address"

This won’t block subdomain-spoofed mail outright, but it will push it to spam — which generates signals you can monitor while you identify whether any legitimate subdomain mail starts getting flagged.

After publishing the change, wait 48 hours for DMARC aggregate reports to reflect the new policy. Review for any unexpected quarantine increases that might indicate a legitimate subdomain you missed. If you see nothing new in quarantine, move to sp=reject.

What this looks like in DMARC reports

The signal you’re looking for after adding sp=reject is subdomain mail appearing in aggregate reports with a reject disposition rather than none. If that mail was legitimate — a sending service you forgot about — it will appear with a volume spike in failing sources.

The riskier outcome is that nothing appears: the attacker-sent mail was being delivered without appearing in reports because a subdomain-specific p=none record (Gap 1) was silently overriding the root, or because a receiver wasn’t applying the RFC default (Gap 3). After adding sp=reject, the legitimate concern shifts from monitoring to confirmation — you want to verify that nothing got newly blocked that shouldn’t have been.

DMARC Guardian’s reports view shows disposition breakdowns per source. After applying sp=reject, the none disposition for subdomain sources should drop to zero. Any remaining volume with reject disposition indicates mail that got blocked — review the IP sources to confirm it’s not a misconfigured legitimate sender.

sp= is one line. Deploy it today.

Most teams skip sp= because nothing prompts them to add it. The DMARC deployment guides focus on the root domain. The monitoring dashboards show root domain health. The three subdomain gaps sit unaddressed in the background until someone points them out.

Adding sp=reject to your existing DMARC record takes five minutes. If your subdomains don’t send email — and you’ve checked — it’s zero risk. If they do send email, finding that out now, in your own audit, is significantly better than finding it out because a customer got a convincing fake invoice.


DMARC Guardian’s DNS tab shows subdomain DMARC coverage for every domain you’re monitoring. If your root domain has p=reject but sp= is missing, the setup guide flags it as an open gap.