SPF, DKIM, and DMARC: Why “Valid” Records Still Let Your Domain Be Spoofed

There are two different questions about your domain's email authentication, and almost every checker answers only the first. The first is do you have SPF, DKIM, and DMARC records — a presence question, a yes/no lookup. The second is do those records actually stop someone from sending email that looks like it came from you — an enforcement question. You can pass the first and fail the second completely, and the gap between them is the whole game: a domain with all three records published, every free checker showing green, that a spammer can still spoof at will because each record is published in its permissive, do-nothing mode.

The permissive modes exist for a good reason — they're how you roll these records out without bouncing your own legitimate mail. The problem is that "published it in monitor mode so I could watch first" and "finished" look identical to a tool that only checks presence, and an enormous number of domains stop at the first and never come back. Here's what actually decides enforcement, record by record, and how to tell which mode yours is in.


SPF: only -all actually rejects

An SPF record lists which servers are allowed to send mail as your domain, and it ends in an all mechanism that says what a receiver should do with a server that isn't on the list. That final qualifier is the entire enforcement decision, and there are four of them:

  • -all (hardfail) — "reject mail from any server not listed." This is the only one that protects you.
  • ~all (softfail) — "accept it but mark it suspicious." Receivers still deliver it. Softfail is the rollout setting, and it's where most records get stranded.
  • ?all (neutral) — "no opinion." Functionally the same as having no policy on the all term.
  • +all — "any server on the internet may send as this domain." This is actively worse than no SPF at all, and it's usually a copy-paste accident.

So an SPF record can be present, syntactically perfect, and end in ~all — and it stops nothing. A checker that reports "SPF: found" tells you nothing about whether the record does its job. The one that matters reads the qualifier on the all mechanism.

There's a second SPF trap that hides even from people who know about -all: the RFC 7208 ten-lookup limit. SPF evaluation is capped at 10 DNS lookups, and every include: (each mail vendor you add — your ESP, your CRM, your helpdesk, your invoicing tool) can cost one or more. Go over 10 and receivers return a PermError and stop evaluating SPF entirely — a record that read as fine yesterday silently stops working the day you added one more vendor. Nothing in your DNS changed to warn you; the count just crossed a line. This is why "we set up SPF once" is not a durable state: SPF strength is a function of how many senders you've bolted on since.

DMARC: p=none is a smoke detector, not a lock

DMARC is the record that ties SPF and DKIM together and tells receivers what to do when a message fails both. Its p= tag is the enforcement switch, and it has three settings:

  • p=none — "check, report, but deliver anyway." This is monitor-only. Forged mail still lands in the inbox. It exists so you can collect DMARC aggregate reports and see who's sending as you before you turn on teeth.
  • p=quarantine — "send failing mail to spam."
  • p=reject — "refuse failing mail outright." This is the goal.

Here is the single most common email-auth state on the internet: a domain publishes a DMARC record, a checker shows "DMARC: present," everyone moves on — and the policy is p=none. Published and unenforced. The record is doing exactly one useful thing (generating reports) and zero protective things (forged mail is still delivered). p=none is a smoke detector: it tells you about a fire, it doesn't put one out. Only p=quarantine or p=reject actually stops a spoof.

Two more DMARC tags quietly undercut a policy that looks enforced:

  • sp= sets the policy for subdomains. A record with a strong p=reject but sp=none leaves every subdomain — mail., news., billing. — fully spoofable, and attackers know to try them.
  • pct= applies the policy to only a percentage of mail. p=reject; pct=20 enforces on one message in five and delivers the other four. It's a rollout dial people forget to turn back to 100.

Presence tells you none of this. Enforcement is p=, sp=, and pct= read together.

DKIM: the leg you can't fully check from the outside

DKIM signs each message with a private key and publishes the matching public key in DNS, so a receiver can verify the message wasn't altered and really came from an authorized sender. It's the third leg of the triad — and it's the one that behaves differently from a checker's point of view, in a way worth understanding before you trust any "DKIM: not found" result.

The catch is the selector. DKIM keys don't live at a fixed DNS name; they live at <selector>._domainkey.<yourdomain>, and the selector is chosen by the sender. Google Workspace uses google, Microsoft 365 uses selector1/selector2, SendGrid, Mailchimp, and the rest each pick their own — and there is no way to enumerate a domain's selectors from DNS. You cannot ask "what are this domain's DKIM keys?" There's no record that lists them.

So any external DKIM check does the only thing it can: it probes a curated list of the well-known selectors the major providers use and reports which ones answer. That makes a hit definitive — "DKIM is configured, here's the selector" — but a miss is not: it means "none of the common selectors resolved," which is a very different statement from "this domain has no DKIM." A domain using a custom selector is signing every message correctly and will show up blank on a selector probe. Read a DKIM "not found" as "verify by hand," never as "broken." (Any tool that reports a flat "no DKIM" from outside is overstating what it can actually know.)

MTA-STS: enforce, not testing

The same presence-versus-enforcement split shows up one layer down, in whether inbound mail to you is encrypted in transit. MTA-STS (RFC 8461) lets your domain tell other mail servers "always use TLS when delivering to me," which defeats a network attacker who'd otherwise strip encryption and read or alter inbound mail. But — exactly like DMARC — publishing the policy is only half of it. The policy file carries a mode:, and only mode: enforce actually forces TLS. A policy in mode: testing is advisory: the receiver reports what it would have done and delivers over plaintext anyway. Declared-but-testing is the MTA-STS equivalent of p=none. (Its companion record, TLS-RPT, just collects failure reports — useful, but it enforces nothing on its own.)

Presence is the easy half. Check enforcement.

If you take one thing from this: "has SPF/DKIM/DMARC" is the wrong test. Every one of these records has a published-but-toothless mode that a presence check reports as green, and those modes are where the overwhelming majority of domains actually sit. Test for enforced, term by term:

  1. SPF — does it end in -all (not ~all/?all/+all)? And is it under the 10-lookup limit, or one vendor away from a PermError?
  2. DMARC — is p= at quarantine or reject (not none)? Is sp= at least as strong? Is pct= at 100?
  3. DKIM — does a known selector resolve? If not, confirm by hand which selector your sender uses before assuming it's missing.
  4. MTA-STS — if declared, is the policy mode: enforce, not testing?

You can walk all four with dig/nslookup and a careful read of each record's qualifiers. If you'd rather not, the free email-authentication checker does exactly this from outside your stack: it parses the all qualifier and counts your SPF lookups, reads the DMARC p=/sp=/pct= and tells you whether the policy is enforced or merely present, probes the well-known DKIM selectors, and reports whether MTA-STS is in enforce or testing — the enforcement distinctions most free checkers skip. No signup, one domain at a time.


The one-line version: publishing SPF, DKIM, and DMARC is the easy half; whether they stop a forgery is decided by a few qualifiers — -all, p=quarantine/reject, mode: enforce — that a presence check never looks at. A domain can show three green ticks and still be trivially spoofable, and the usual reason isn't negligence, it's a rollout in monitor mode that nobody ever finished.

And enforcement isn't a one-time state. Adding a mail vendor can push SPF over the 10-lookup line into a silent PermError; a migration can relax DMARC back to p=none "just while we test"; an MTA-STS policy can lapse — and none of it announces itself. Merlonix watches email-auth posture the way it watches SSL and DNS: continuously, from outside your infrastructure, and alerts you when a policy you relied on weakens — so p=reject quietly becoming p=none is something you hear about from us, not from a customer who got a convincing invoice from "you." Run the free email-auth scan to see where a domain stands right now, and browse the rest of the free tools while you're there.