Home Blog Contact
Home/Blog/Why SPF Passes a Checker but Outlook Says Jun…
How toEmail MarketingSPFDeliverabilityDNS

Why SPF Passes a Checker but Outlook Says Junk

9 min readBy Miloš Mitrović

If a checker reports SPF pass but Outlook keeps filing your campaigns in Junk, the first thing to test is your DNS lookup count. An SPF record that parses cleanly still fails at evaluation time once nested vendor includes push it past ten DNS mechanisms, and the result is permerror rather than pass. Most checkers hide this because they validate syntax, or they test the envelope sender your ESP owns instead of the From domain you publish. The diagnostic is to count mechanisms in the fully expanded record rather than trusting a green pass flag.

Key takeaways

  • SPF evaluation must stop at ten DNS-querying mechanisms, and nested includes inside your vendors' records count against your budget, not theirs.
  • Exceeding the limit returns permerror. Permerror is not a fail, so DMARC still passes on aligned DKIM and your aggregate reports look healthy while Outlook filters you.
  • Two void lookups (a query returning NXDOMAIN or an empty answer) are enough to force permerror even in a record that only costs six lookups.
  • ip4, ip6 and all cost nothing. include, a, mx, ptr, exists and redirect each cost one, plus everything they nest.
  • Delegate each vendor to its own sending subdomain instead of flattening your root record into a list of IP blocks you then have to maintain by hand.
  • Re-count on a schedule. Vendors change their own includes without telling you, and a record sitting at nine lookups goes to eleven with no change on your side.

Why a record that reads as valid fails at evaluation

Because the limit applies to the expanded record at the moment a receiver evaluates it, not to the text you published. RFC 7208 section 4.6.4 requires implementations to limit a single SPF check to ten mechanisms and modifiers that cause DNS queries: include, a, mx, ptr, exists and the redirect modifier. Every one of those inside a vendor's record counts too. A root record with six includes looks modest until you resolve them and find that one of those vendors publishes three includes of its own.

The consequences are counted, not estimated. If the eleventh lookup is required, evaluation stops and returns permerror, defined in section 2.6.7 as a domain whose published records could not be correctly interpreted. Your mail is not authenticated by SPF at that point, for every recipient, on every send. Nothing in your ESP dashboard changes.

Two smaller caps live in the same section and catch people who think they have headroom. The a and mx mechanisms are each limited to ten address records, and mx is limited to ten MX records, so a domain with a large MX set can permerror on a single mechanism. Google states the same ten lookup ceiling in its email sender guidelines, and Microsoft repeats it in its SPF setup documentation.

Why your checker reported a pass

Three different reasons, and it is worth knowing which one fooled you. First, many web checkers parse the record and report syntax validity. Syntax is fine in the failure we are describing, so they show green. Second, some checkers resolve includes but stop counting at the top level, or they cache the vendor records they resolved yesterday and miss the include a vendor added this week.

Third, and this is the one that misleads most operators, SPF authenticates the envelope MAIL FROM (the Return-Path), not the From header your subscribers see. If you send a test through your ESP and inspect the headers, you may be reading a pass for the ESP-owned bounce domain while your organizational domain's record is the broken one. Send a test to a mailbox you control, open the full headers, and read which domain the SPF result is attached to. If it is not the domain in your visible From address, that pass tells you nothing about your own record. See RFC 7208 for the identity definitions.

How to count the lookups your record actually costs

Resolve your record, then resolve every include it names, one level at a time, until nothing new appears. Count each include, a, mx, ptr, exists and redirect you meet along the way.

dig +short TXT brand.com

for d in $(dig +short TXT brand.com | grep -oE 'include:[a-z0-9._-]+' | cut -d: -f2); do
  echo "== $d"
  dig +short TXT "$d"
done

Repeat the loop against each domain the output reveals. It is tedious by design, because the tedium is exactly what the checker skipped. If you want a second opinion, dmarcian's SPF Surveyor expands the tree and reports a count you can compare against your own.

Typical costs to expect, which you should verify in your own DNS rather than trust here, since vendors revise their records:

MechanismTypical DNS lookups
include:_spf.google.com4 (itself plus three nested netblock includes)
include:spf.protection.outlook.com1 (flat IP list)
Marketing, transactional, helpdesk or CRM vendor include1 to 3 each
a, mx, ptr, exists1 each, before their own sub-limits
ip4, ip6, all0

Google Workspace plus Microsoft 365 plus four vendors at two lookups each is already over the line. That is the shape of most records I audit.

The void lookup cap that fails a short record

A record well under ten lookups can still permerror if it contains dead includes. Section 4.6.4 also says implementations should limit void lookups, meaning queries that return NXDOMAIN or a NOERROR response with no answer, to two, and treat the excess as permerror. Two decommissioned vendors left in your record are enough.

This is the failure mode that appears after an ESP migration or an agency handover. Somebody moved off a review platform two years ago, the include stayed, the vendor retired the hostname, and your record now burns void lookups on every evaluation. When you audit, note any include whose TXT query returns nothing at all. Those are not harmless leftovers. Each one is a third of your void budget.

Why Outlook punishes permerror when Gmail appears not to

Because permerror is not a fail, so nothing rejects, and each receiver decides on its own how much weight to remove from an unauthenticated result. DMARC passes when either SPF or DKIM passes with an aligned identifier, per RFC 7489 section 3.1. If your DKIM signature is aligned and valid, which it is for any properly configured Klaviyo or Shopify sending domain, DMARC keeps reporting pass. Your aggregate reports stay green. Your policy never bites. The SPF permerror sits in the spf column, unread.

Microsoft's filtering is where the cost shows up. Outlook.com and Exchange Online lean harder on sender reputation for bulk mail, and an authentication result that is neither pass nor fail contributes nothing positive, so borderline campaigns tip into Junk while Gmail, with more history on your domain and heavier weight on engagement, keeps delivering to the inbox. That asymmetry is why the complaint arrives as "our Outlook subscribers stopped opening" rather than as a bounce. If your symptoms differ per recipient inside the same provider instead, the problem is more likely alignment than lookup count, and the walkthrough in diagnosing DMARC alignment failing per recipient is the better starting point.

How to get back under the limit without breaking a vendor

Work in this order, because the first two steps are free and the third has ongoing cost.

  1. Delete dead includes. Any include that resolves to nothing, or belongs to a tool you no longer send from, comes out. This alone fixes a surprising number of records.
  2. Move vendors onto sending subdomains. A vendor that sends from mail.brand.com or send.brand.com uses the SPF record published on that subdomain, so it costs your root record nothing. Klaviyo's dedicated sending domain setup already works this way through CNAME delegation, which is why a correctly configured Klaviyo account rarely appears in a root SPF record at all. Push transactional, helpdesk and CRM senders the same way. If you run several brands from one DNS zone, decide the pattern deliberately using sending domain strategy for multi-brand Klaviyo.
  3. Flatten only as a last resort. Replacing an include with the vendor's current ip4 and ip6 blocks removes the lookup cost and transfers the maintenance to you. When that vendor rotates IP space, your SPF starts failing for genuine mail and nobody gets an alert. Note also that each TXT string caps at 255 characters and long records risk oversized DNS responses, so a flattened record has its own ceiling. If you flatten, automate the refresh and monitor it.

One thing not to do: publish two SPF records for the same name. That is permerror regardless of lookup count, and it is a common accident when a second vendor's setup wizard adds its own TXT record instead of editing yours.

How to keep it from silently returning

Put the lookup count on a schedule, because your record can break without anyone touching it. A vendor adding a nested include to its own SPF record moves you from nine lookups to eleven, and you find out from a retention report three weeks later. Run the expansion weekly against every sending domain and subdomain you own, alert on a count of nine or higher, and alert on any include that returns an empty answer.

Then read the spf result in your DMARC aggregate reports rather than the overall pass rate. The pass rate is the number that hides this. What you want is the share of your mail where SPF specifically evaluated to pass with an aligned domain, tracked over time. Pair it with Google Postmaster Tools for domain reputation, and treat a drop confined to one provider as a routing signal rather than a content problem. The broader checklist in why store emails land in spam covers the adjacent causes worth ruling out at the same time.

Trade-offs and what I would do

The genuine decision is delegation versus flattening, and I recommend delegation in almost every case. Flattening is faster today and quietly fragile: it converts a vendor's problem into your problem, and the failure it produces later is worse than the one you fixed, because a stale IP block fails legitimate mail with a hard SPF fail rather than a permerror. Subdomain delegation costs more setup, gives each vendor an independent budget, and has the side benefit of separating your marketing reputation from your transactional reputation.

The cost to be honest about is that a new sending subdomain has no history. Moving a high-volume vendor onto a fresh subdomain to save lookups will dent placement for a few weeks unless you ramp it, so schedule that change away from your peak season and follow a warmup plan such as the one in warming a new sending domain. If you are two lookups over and it is the week before Black Friday, flatten the smallest, most stable vendor as a temporary measure, write the removal date in the ticket, and do the delegation properly in January.

The other trade-off worth naming: some teams keep every vendor in the root record because it feels tidier to have one place to look. It is not tidier. It is a shared budget with no owner, and the ten lookup ceiling means the last team to add a tool breaks authentication for every team that added one before them.

Sources

M
Miloš Mitrović
Email Marketing for Ecommerce

Have a question or a project?

Whether it is about this post or a system you want built, I'm happy to talk.

Get in touch

404

Post not found. It may have been moved or the link is incorrect.

← Back to the blog
Summarize with AI
ChatGPT, Perplexity, and Grok open with the prompt ready to run. Claude, Gemini, and Copilot open a chat with the prompt copied; press Ctrl+V (Cmd+V on Mac) to paste. The full text is included, so it works even without web access.