Your SPF record looks fine. It's been working for months. Then one day, mail to Gmail or Outlook starts bouncing, or worse, it just quietly lands in spam with no bounce at all. You check the SPF record and it's still there, still syntactically correct — so what broke?

Nine times out of ten, the answer is the SPF 10-lookup limit. It's one of the most common email deliverability problems we see, and it's almost invisible until it isn't.

Symptom

Some signs you're hitting this:

  • Mail delivered fine for months, then suddenly starts failing SPF checks after you added a new sending service (SendGrid, Mailgun, Zoho, a CRM, a helpdesk tool).
  • Running an SPF checker shows permerror instead of pass or fail.
  • DMARC reports (the XML files you get if you have a rua tag set) show SPF results of permerror for legitimate sending IPs.
  • Google Postmaster Tools or Microsoft SNDS shows a spike in spam-foldered or rejected mail with no obvious cause.

The frustrating part is that a permerror doesn't behave like a normal SPF failure. Some receiving servers treat it as a hard fail. Others silently ignore your entire SPF record, which effectively disables your SPF protection and makes spoofing easier — the opposite of what you wanted.

Cause: the RFC 7208 10-Lookup Ceiling

SPF has a hard rule baked into RFC 7208: a receiving mail server is only required to perform 10 DNS lookups while evaluating your SPF record. Once that limit is exceeded, the whole check returns a permanent error — not a fail, a permerror, which is arguably worse.

The lookups that count toward this limit are:

  • include: mechanisms (each one is a lookup, plus whatever lookups happen inside it)
  • a and mx mechanisms (each triggers a DNS lookup to resolve)
  • ptr mechanisms (rarely used now, and expensive)
  • exists mechanisms
  • The initial lookup of the SPF record itself doesn't count, but every mechanism inside it that needs its own DNS query does

Here's the trap: ip4: and ip6: mechanisms are free — they don't need a lookup because the IP is already right there in the record. But include: mechanisms are the silent budget-killer, because each one can itself contain more includes.

A typical record that creeps past the limit looks like this:

v=spf1 include:_spf.google.com include:sendgrid.net include:mailgun.org include:spf.zoho.com include:amazonses.com include:_spf.salesforce.com include:servers.mcsv.net a mx ~all

That looks like eight includes plus a and mx — already at 10. But include:_spf.google.com alone can resolve through several nested includes internally. By the time you count the full chain, you can blow past 10 lookups with what looks like a modest, tidy record.

How to Check Your Actual Lookup Count

Don't eyeball it — count it properly. A few reliable ways:

  • Use an SPF-specific checker like MXToolbox's SPF record check or dmarcian's SPF Surveyor. Both walk the full include chain and tell you the exact count.
  • From a Linux box or your VPS, you can trace it manually with dig:
    dig TXT yourdomain.com +short
    dig TXT _spf.google.com +short
    dig TXT sendgrid.net +short
    and follow each include down the chain, counting as you go. Tedious, but it works when you don't trust a third-party tool.

If you're anywhere close to 10, treat it as already broken — different resolvers and different receiving mail servers can count slightly differently depending on caching and edge cases, so a count of 9 isn't a safe margin.

Fix: Flatten and Consolidate

You have three real options, and you'll usually use a mix of the first two.

1. Drop services you don't actually send through

This sounds obvious, but it's the most common fix we apply. Old SPF records accumulate includes from tools that were tested once, or a marketing platform nobody uses anymore, or a helpdesk that got replaced two years ago. Pull up your record and ask, for every include:, whether you currently send outbound mail through that service. If not, delete it.

2. Replace a and mx with explicit IPs

If your record includes a or mx just to authorize your own mail server, and that server has a static IP (which it should, on a VPS), replace it:

# Instead of this:
v=spf1 a mx include:_spf.google.com ~all

# Do this:
v=spf1 ip4:203.0.113.10 include:_spf.google.com ~all

This removes two lookups for the cost of zero, since ip4: mechanisms don't count against the limit.

3. SPF flattening for third-party services

For services you can't drop, flattening means resolving their include chain once, grabbing the actual IP ranges, and hardcoding those as ip4:/ip6: mechanisms instead of an include:. This gets you under the limit immediately.

The catch: those IP ranges can change on the provider's end without warning you. If you flatten manually, you need a process to re-check periodically (quarterly is reasonable for most providers), or your SPF will silently go stale and start rejecting mail from IPs the provider added after you flattened. Some DNS providers offer automatic SPF flattening as a managed feature specifically to handle this churn — worth it if you have five or more third-party senders.

Updating the Record in cPanel

If your domain's DNS is hosted in cPanel (Zone Editor), the process is:

  1. Log in to cPanel → Zone Editor.
  2. Find the TXT record starting with v=spf1 for your domain.
  3. Click Edit, replace the value with your consolidated record, and save.
  4. Only ever have one SPF TXT record per domain. Two separate v=spf1 records is itself an RFC violation and causes a permerror on its own, independent of the lookup count.

Give it 15–30 minutes to propagate before re-testing, since TXT records typically use a short TTL but resolvers still cache.

Prevention

  • Before adding a new sending tool (CRM, marketing platform, transactional email API), check what SPF include it requires and re-count your lookups before you add it, not after mail starts bouncing.
  • Audit your SPF record every time you decommission a service, not just when you add one. Stale includes are the main reason records grow past the limit in the first place.
  • If you're managing SPF for a domain with many outbound services, set a calendar reminder to review the record quarterly. It's a five-minute check that prevents a multi-day deliverability outage.
  • Consider consolidating outbound mail through fewer providers where practical — every additional sending service is another include, another point of failure, and another thing to keep flattened.

Frequently Asked Questions

Does SPF permerror mean all my mail bounces?

Not always, and that's part of what makes it dangerous. Behavior varies by receiving server. Some treat a permerror as a hard fail and reject the message. Others ignore SPF entirely when it errors out, which quietly removes that layer of protection without any visible bounce. Either way, it's not something to leave unresolved.

Do ip4 and ip6 mechanisms count toward the 10-lookup limit?

No. Only mechanisms that require a DNS query count — include, a, mx, ptr, and exists. Hardcoded IP mechanisms are free, which is exactly why flattening works as a fix.

Can I just use a stricter all qualifier instead of fixing the lookup count?

No, that doesn't help. The ~all (softfail) or -all (hardfail) qualifier only matters if SPF evaluation completes successfully. If you're already over 10 lookups, evaluation aborts with a permerror before it ever reaches the all mechanism.

How often do third-party providers change their SPF IP ranges?

It varies by provider, but ranges used for flattening should be treated as changeable at any time without notice. If you flatten manually, review and re-flatten every few months. If a provider offers a maintained SPF include instead of hardcoded IPs, that include is easier to trust long-term — the tradeoff is it costs you a lookup.

Will fixing SPF alone fix my DMARC alignment?

Not by itself. SPF passing is one half of the equation. DMARC also checks DKIM, and it requires that either SPF or DKIM pass and align with the domain in the From header. Fixing a broken SPF record removes one common cause of DMARC failures, but review your DKIM signing too if DMARC reports still show failures after this fix.