Skip to content
dmarc.quest

Email Authentication Misconceptions That Engineers Hold Too

SPF, DKIM and DMARC each check something narrower than it looks, and the gaps catch people who have read the specs. Fourteen beliefs that don't hold up, what the RFCs actually say, and what to do about each.

It's natural to describe SPF as checking whether a sender is allowed to use your domain. What it actually checks is whether one server may use one name, in an envelope address the recipient never sees. DKIM and DMARC are just as specific, and each one leaves out something it's easy to assume it covers.

The items are grouped by protocol, with subdomains and transport getting sections of their own. Each spec claim links to the RFC section it comes from.

SPF

1. "SPF stops people forging my From address"

SPF checks the envelope sender, the address in the SMTP MAIL FROM command that bounces go back to, and optionally the server's HELO name (RFC 7208 §2.3–2.4). It never looks at the From line your recipient sees.

A forger can use an envelope domain they control, pass SPF for it, and put your domain in From. The RFC says as much: a message that passes SPF can still carry other false identities (§11.2).

SPF was built for a different problem. It lets a receiver refuse a forged envelope sender during the SMTP conversation, and it gives receivers a domain to build reputation on instead of an IP address (§1). Connecting authentication to the visible From is DMARC's job.

2. "Every service I use needs an include in my SPF record"

SPF evaluates the record of the envelope sender's domain. If a service returns bounces to an address on its own domain, or on a subdomain of yours with its own SPF record, a receiver checking that service's mail looks at that record, not your root. The include you added to your root may play no part in that service's mail.

That include still has effects. It counts toward the 10-lookup limit (item 3) whenever evaluation of your root record reaches it. And it extends what your root record authorizes: wherever the service's own SPF policy passes for a server, that server now passes for mail using your root domain as its envelope sender. SPF doesn't check which of the service's customers sent the message. RFC 9989 asks domain owners to review their SPF records to understand "which networks are authorized to send on behalf of the Domain Owner" before publishing DMARC (§7.1).

What to do: for each service, look at the return path on a real message from it, then check which SPF record that domain actually uses, following any include or redirect chain. An include at your root that no service's mail reaches is cost without benefit.

3. "The 10-lookup limit counts includes"

It counts every term that makes a DNS query: include, a, mx, ptr and exists, plus the redirect modifier. Terms inside included records count too, all the way down. ip4, ip6 and all cost nothing. Going over the limit is a permerror (RFC 7208 §4.6.4).

The same section adds two lesser-known limits. Evaluating each MX record may not query more than 10 A or AAAA records, or the mx mechanism is a permerror. And implementations SHOULD limit "void lookups", queries that return no answers or a nonexistent name, to two, with going over the limit a permerror.

A permerror means SPF can't pass for that message. It doesn't automatically fail DMARC, because an aligned DKIM signature can still pass on its own (RFC 9989 §4.4). But any sender relying on SPF alone stops passing.

4. "Two SPF records are fine; receivers merge them"

They don't. More than one SPF record at the same name is a permerror (RFC 7208 §4.5). One way it happens: a second service's setup guide says "add this TXT record", and nobody merges it into the existing one.

Splitting a single long record into several quoted strings within one TXT record is fine. Receivers join them (§3.3).

5. "-all is stricter, so it's safer"

RFC 9989 warns against exactly this (§7.1). Some receivers check SPF before DMARC, so a -all fail can reject a message early in the SMTP transaction, "before any DMARC processing takes place". That includes messages that would have passed DMARC on an aligned DKIM signature. Forwarded mail is exposed to this: the forwarder relays from its own IP, SPF fails, and an intact DKIM signature never gets checked.

The worse part is that you can't see it. Those rejected messages "will never appear in aggregate DMARC reports", because the transaction never reaches the point where the From domain is revealed.

What to do: on domains that send mail, consider ~all. It reduces the chance of an SPF-only rejection before DMARC can evaluate a surviving DKIM signature, though receivers keep the final say. On domains that send no mail at all, publish v=spf1 -all, the record RFC 7208 gives for exactly that case (§10.1.2).

DKIM

6. "A DKIM pass means the message came from the domain in From"

A DKIM pass means a signature associated with the domain in its d= tag verified: the signed content matches what was signed, after the canonicalization the signer chose, which tolerates some changes such as whitespace (RFC 6376 §3.4). The From header is always signed (§5.4). But nothing in DKIM requires d= to have anything to do with the From domain. A sending service can sign with its own domain until you configure a custom one.

The signer is "claiming responsibility for an introduction of a message into the mail stream" (§3.5). That doesn't establish who wrote the message or whether you authorized it. DMARC's alignment check is what connects the two (RFC 9989 §4.4).

7. "You can check DNS to see whether a domain uses DKIM"

DKIM keys live at <selector>._domainkey.<domain>, and the sender chooses the selector name (RFC 6376 §3.1). A verifier checking a message doesn't need to guess: it reads s= and d= from the signature and queries that one name (§3.6.2.1). But there's no record listing a domain's selectors, so a check that only has a domain name, ours included, probes a list of candidate names.

A key record found that way proves a key is published, not that anything signs with it. A record with an empty key is a revoked key (§3.6.1). A miss only means "no key at the names we tried". To learn which selector a sender actually uses, read the s= tag on a message it signed.

Subdomains

8. "My subdomains are covered by my root's SPF and DKIM"

SPF and DKIM records are looked up at the exact name being checked. Neither is inherited by subdomains. DMARC policy works differently. A subdomain without a valid DMARC record of its own uses the record found at its organizational domain, or failing that at its public suffix domain (RFC 9989 §4.10.1). That record's sp= and np= tags can set a different policy for subdomains than p= does for the root.

That doesn't mean mail from news.example.com is doomed if the subdomain has no SPF or DKIM records of its own. Under relaxed alignment, the default, a passing DKIM signature with d=example.com aligns with a From of news.example.com, because both share the organizational domain example.com (§4.4). What matters is which name each sender actually authenticates on, not which names have records.

9. "Delegating a subdomain to a vendor only affects that subdomain"

Relaxed alignment works in the other direction too. Whoever controls SPF or DKIM records for a subdomain under your organizational domain can send mail with your root domain in From and get a DMARC pass, as long as alignment for that mechanism is relaxed (RFC 9989 §11.8). The RFC's example is an attacker who controls evil.example.com, publishes SPF there, and sends as foo@example.com.

That makes delegating a subdomain to a vendor, an agency or another team a trust decision about your whole domain. The RFC's advice is to avoid delegating subdomains if this is a concern, and to consider strict alignment (adkim=s, aspf=s), which accepts only an exact name match. Before you switch, check that every legitimate sender has at least one mechanism that passes on the exact From domain.

DMARC

10. "I published a DMARC record, so I'm protected"

p=none is a monitoring mode. RFC 9989 says a p=none policy "MUST NOT modify existing mail handling processes" (§5.4). It requests no DMARC-based change to how mail is handled, and receivers' other filtering applies as before.

There's a subdomain trap here too. A subdomain that publishes its own record uses that record instead of its parent's (§4.10.1). So p=none at mail.example.com replaces a p=reject it would otherwise have inherited from example.com.

What p=none gives you depends on the rest of the record. A rua= address requests aggregate reports from participating receivers, normally daily, describing the sources and authentication results they saw. That's how you find legitimate senders that fail before you turn on enforcement. Coverage is incomplete, since only receivers that choose to report do. Without rua= you get no aggregate reports. A ruf= tag can still request message-specific failure reports, specified in RFC 9991, which receivers may decline to send or group together.

p=none does meet one requirement. Gmail (since February 2024) and Outlook.com (since May 2025) require senders of 5,000 or more messages a day to personal Gmail accounts or Outlook.com consumer addresses, respectively, to publish a DMARC record, alongside their other requirements. p=none meets the policy minimum (Google, Microsoft). Google also keeps a domain classified as a bulk sender after its volume drops.

11. "p=reject means forged mail as my brand gets rejected"

It applies to mail with your exact domain in From, and to your subdomains as far as the sp= and np= tags (item 8) say. It does nothing about lookalike domains, a display name like "Example Support" on someone's free webmail address, or a real account that has been compromised.

Even for your own domain it's a request, not a command. RFC 9989 says receivers MAY accept mail that fails DMARC even under p=reject, and SHOULD NOT reject solely because the policy says reject (§5.4). The same section says a DMARC pass "does not carry an explicit or implicit value assertion" about the message: passing means the use of your domain was authorized, not that the mail is safe or will be delivered.

12. "DMARC reports show whether my mail reached the inbox"

The disposition field in an aggregate report is "The result of applying the DMARC policy" (RFC 9990 §3.1.1.9). Its permitted values are none, pass, quarantine and reject (Appendix A). None of them describes where the message landed. A message that passed DMARC and went straight to spam looks like any other pass.

The standard format has no inbox-placement field. The RFC notes that extensions could carry one and calls that "very much discouraged", because it would reveal how a receiver filters (§8.3). And as item 5 explains, mail rejected on SPF before DMARC runs doesn't appear at all.

13. "Reporters won't send to an unauthorized report address"

When your rua= address is at a different organizational domain from the domain whose policy it is, even one you own, the receiver must check for an authorization record. It looks at <policy-domain>._report._dmarc.<destination-host>, and a wildcard record there counts. Without one, "a positive determination of the external reporting relationship cannot be made" (RFC 9990 §4).

We tested this on our own domain. From June 10 to September 17, 2026, we published a second rua= at another domain we control, deliberately without the authorization record, alongside our normal, authorized report address. When we measured on August 8, Google had delivered to the unauthorized address every day throughout the observed period, and Microsoft had delivered at least one report. Yahoo and smaller reporters never reported on that domain during the test, so we can't say what they do.

That's one domain and two reporters, not a survey. But it means report arrival doesn't prove the authorization record is in place. Its absence doesn't diagnose a missing record either: a reporter that does enforce simply skips the unauthorized address and keeps sending to any authorized ones. You can't tell from the receiving end which reporters enforce, so publish the authorization record anyway.

Transport and delivery

14. "No MX record means the domain can't receive mail"

When a domain has no MX record, SMTP falls back to its A or AAAA record and tries to deliver there (RFC 5321 §5.1). To say a domain accepts no mail, publish a null MX: a single MX record with preference 0 and target . (RFC 7505 §3).

And three short ones

  • "Mail between servers is encrypted now." Without an enforced transport policy, STARTTLS is opportunistic. An attacker who can delete the 250 STARTTLS response, or redirect the session, can downgrade or intercept it (RFC 8461 §1). A sender that supports MTA-STS or DANE can require authenticated TLS, using a receiving domain's MTA-STS policy in enforce mode or its DNSSEC-signed DANE records. An attacker can also block MTA-STS policy discovery itself (§10.2). Either way, TLS protects each hop, not the message end to end.
  • "250 OK means it was delivered." The final 250 after the message data means the receiving server accepts responsibility for delivering or relaying it (RFC 5321 §6.1). It says nothing about inbox placement. Accepted mail may go to spam without any bounce. RFC 5321 says silently dropping messages should be considered only with "very high confidence that the messages are seriously fraudulent or otherwise inappropriate" (§6.2).
  • "DMARC is RFC 7489." As of 2026 it's RFC 9989 (core), 9990 (aggregate reports) and 9991 (failure reports). The pct= tag is gone, and policy discovery now uses a DNS tree walk instead of the Public Suffix List. We covered what changed in DMARCbis: what changed.

See your own setup

Several of these only show up when you look at a specific domain: which return path each service uses, which selectors are signing, which subdomains authenticate where. Run a free check on your domain to see its SPF lookup count, the DKIM selectors we can find, and how its DMARC policy applies to its subdomains.