Summary

When your emails consistently fail to reach their intended inboxes, the often-overlooked culprit can be a seemingly minor technical detail: an SPF record exceeding the 10-DNS-lookup limit. This hard cap, enforced by all major email providers, transforms a simple configuration oversight into a permanent authentication failure, severely impacting deliverability. To ensure your high-volume sending achieves its promised 90%+ inbox placement, it's critical to understand and implement SPF flattening, a complex but essential process that SpamCipher's owned deliverability pipeline manages seamlessly, guaranteeing your messages bypass this common pitfall.

When your SPF record triggers more than 10 DNS lookups, receiving servers return a permanent error and your mail fails authentication. This is not a soft warning. RFC 7208 section 4.6.4 mandates the hard limit, and every major receiver enforces it. The fix requires flattening your record by replacing recursive includes with their IP addresses, but doing it wrong breaks your authentication entirely.

Why the 10-lookup limit exists

The limit exists to prevent DNS amplification attacks and infinite loops. An SPF record can include other domains, and those domains can include others. Without a cap, a malicious actor could construct a chain that forces a receiving server to query hundreds of nameservers, creating a denial-of-service condition.

The count includes every mechanism that triggers a DNS query: include:, a, mx, exists:, and redirect=. A single include: counts as one lookup, but if that included domain itself contains three more include: statements, you have consumed four lookups total. The ip4: and ip6: mechanisms cost zero lookups because they are literal addresses.

When you exceed ten, the receiving server stops processing and returns permerror. This is worse than softfail or neutral. A permanent error means the authentication check failed completely, and many receivers will reject or heavily penalize the message. You cannot negotiate or configure your way around this. The limit is in the standard.

How to count your lookups correctly

Start with your published SPF record. For each mechanism, apply these rules:

  • include:domain.com costs one lookup, then recursively count every mechanism in domain.com's SPF record.
  • a or a:domain.com costs one lookup (the A record query).
  • mx or mx:domain.com costs one lookup (the MX record query).
  • exists:domain.com costs one lookup.
  • redirect=domain.com costs one lookup, then recursively count the target record.
  • ip4: and ip6: cost zero.
  • ptr costs one lookup, but do not use ptr. It is deprecated and slow.

Example record:

v=spf1 include:_spf.google.com include:spf.protection.outlook.com include:sendgrid.net ip4:203.0.113.10 ~all

Google's _spf.google.com contains three nested includes. Outlook's record contains two. SendGrid's contains one. Total: 1 + 3 + 1 + 2 + 1 + 1 = 9 lookups. You are one include away from failure.

Most operators discover the problem only after adding a new service. The record worked yesterday, you added Mailchimp or Zendesk today, and suddenly authentication breaks. This is why you must count before publishing.

Flattening: replace includes with IP ranges

Flattening means resolving an include: to its final IP addresses and replacing the include with ip4: or ip6: mechanisms. This trades lookup count for record length.

Take include:_spf.google.com. Query it:

dig TXT _spf.google.com

You get:

v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all

Query each netblock:

dig TXT _netblocks.google.com

Returns:

v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ... ~all

Repeat for _netblocks2 and _netblocks3. Collect all IP ranges. Replace include:_spf.google.com in your record with the full list of ip4: and ip6: blocks.

Your lookup count drops by three (the original include plus its two nested includes), but your record grows by several hundred characters. SPF records have a 255-character limit per DNS TXT string, and a practical limit around 450 characters for the entire record before some resolvers truncate. If flattening pushes you past that, you must split the record or remove services.

The failure mode: if Google adds a new netblock and you have flattened, your record is stale. Mail from the new range fails SPF. This is why flattening is a trade-off, not a pure win. You must monitor the upstream records and update yours when they change.

Partial flattening and service priority

You do not have to flatten everything. Flatten only the services whose IP ranges change infrequently. Google and Microsoft publish large, stable netblocks. Transactional ESPs like SendGrid and Postmark change their ranges more often, especially as they scale infrastructure.

Prioritize by send volume and criticality. If you send 10,000 messages a month through Google Workspace and 500 through a marketing tool, flatten the marketing tool first. Its lookup cost is the same, but its IP stability is lower and its volume matters less. If it breaks, fewer messages are affected.

A worked example: you have nine lookups and need to add a tenth service. Flatten your lowest-volume ESP. If that ESP uses two includes (one for the main record, one nested), you drop two lookups and gain two slots. Add your new service and you are at eight lookups total.

Some services provide a pre-flattened record. Postmark publishes spf.mtasv.net, which is already a flat list of IP ranges. Use that instead of an include-heavy record when available.

For a deeper look at SPF syntax and how each mechanism behaves, see our SPF record syntax guide.

Automating flattening and detecting drift

Manual flattening breaks the moment an upstream provider changes their ranges. You need automation. Write a script that queries each included domain daily, compares the returned IP ranges to your published record, and alerts on drift.

Pseudocode:

for each include in your SPF:
  resolve include to IPs
  compare to your published IPs for that service
  if mismatch: alert

Run this as a cron job. When it fires, update your record and republish. The update lag is your risk window. If Google adds a netblock Monday and you update Friday, mail from that netblock fails SPF for four days.

Some operators use dynamic SPF services that maintain the flattened record for you. These services poll upstream records, detect changes, and update your DNS automatically via API. This works if you control your DNS programmatically. If you manage DNS through a registrar web UI, you are back to manual updates.

The alternative is to accept the 10-lookup limit as a hard constraint and remove services. If you send through six ESPs and each costs two lookups, you cannot add a seventh without flattening or cutting one. This is a forcing function for consolidation, which often improves deliverability anyway. Fewer sending sources mean simpler reputation management and less configuration drift.

When to use redirect instead of include

The redirect= modifier replaces your entire SPF policy with another domain's policy. It costs one lookup, and the target record's lookups count against your total. Use it when you delegate all SPF logic to a single provider.

Example: you send exclusively through Microsoft 365. Instead of:

v=spf1 include:spf.protection.outlook.com ~all

Use:

v=spf1 redirect=spf.protection.outlook.com

The lookup count is identical, but the semantics are clearer. The redirect= says "I defer entirely to this domain." If you later add another service, you must remove the redirect and switch back to includes.

Do not use redirect= if you have any ip4: or ip6: mechanisms in your own record. The redirect replaces everything, so those IPs are ignored. This is a common mistake. Operators add a redirect, forget they have a local IP range, and that range suddenly fails SPF.

The redirect= must be the last item in your record. If you put it before other mechanisms, those mechanisms are processed first, and the redirect only applies if none of them match. This is almost never what you want.

Testing your flattened record before publishing

Before you publish a flattened record, test it. Use a command-line SPF validator:

dig TXT yourdomain.com | grep spf

Then run an SPF checker that counts lookups. Several online tools do this. Verify the count is under ten and that all your known sending IPs are covered.

Send test messages from each service and check the Authentication-Results header in the received mail. Look for:

spf=pass smtp.mailfrom=yourdomain.com

If you see spf=permerror, you still exceed ten lookups or you have a syntax error. If you see spf=fail, an IP range is missing from your flattened record.

A common error: flattening Google but forgetting that Google Workspace uses different netblocks than Gmail. If you only flatten the Gmail ranges, mail sent through Workspace fails. Query _spf.google.com and flatten all three netblock includes, not just one.

Another common error: flattening an ESP that uses multiple domains. SendGrid, for example, has sendgrid.net and several regional subdomains. If you flatten only the main domain, mail from a regional server fails. Check the ESP's documentation for the canonical SPF include and flatten that.

For broader context on how SPF fits into modern authentication, see our guide on cold email deliverability and authentication.

How SpamCipher handles SPF complexity

SpamCipher is the cold email platform for unlimited, automated sending, so SPF correctness is table stakes it handles for you: it validates your SPF record during domain setup and flags lookup count issues before you send. If you are over the limit, the platform shows exactly which includes are consuming lookups and suggests which services to flatten based on your sending patterns.

For customers who flatten, SpamCipher monitors upstream SPF records daily and alerts when a provider changes their IP ranges. You get a notification with the exact diff, so you can update your DNS before mail starts failing. This eliminates the silent breakage that happens when you flatten manually and forget to check for updates.

The platform also tracks SPF authentication results across your sends. If a subset of messages suddenly shows permerror or fail, you see it in the dashboard with the affected IPs highlighted. This makes it obvious when a flattened record has drifted or when a new service is sending from an IP you have not authorized.

Frequently asked questions

No. The 10-lookup limit is defined in RFC 7208 and enforced by all major receivers. There is no configuration or DNS trick that raises it. You must flatten your record or remove services.
Receiving servers return a permanent error (permerror) and your mail fails SPF authentication. Many receivers will reject the message outright or route it to spam. This is not a soft failure.
Whenever an upstream provider changes their IP ranges. Google and Microsoft change infrequently, but smaller ESPs can change monthly. You must monitor the upstream records and update yours when they drift, or use an automated service that does this for you.

See where your domain stands

Run the free SpamCipher check and see exactly which authentication and reputation gaps apply to your sending domain.

Get started free