Sender Policy Framework (SPF) is one of the oldest pieces of email authentication still doing real work. You publish a DNS TXT record that lists which mail servers and IP addresses are allowed to send mail for your domain. When a receiving server gets a message from [email protected], it looks up your SPF record and checks whether the sending IP is on the list. Match, and the message passes. No match, and it gets flagged or bounced.
Years ago this was simple. Most companies ran their own mail server, so the record held one IP or a single a reference and that was the end of it. Now a mid-sized company might route mail through a dozen platforms at once: Google Workspace or Microsoft 365 for mailboxes, HubSpot or Salesforce for CRM, Mailchimp or Klaviyo for marketing, Zendesk for support, Stripe or SendGrid for receipts. Every one of them hands you an include: statement to paste in, like include:_spf.google.com.
Here is the problem. RFC 7208 caps SPF evaluation at 10 DNS lookups. Cross that line and the receiving server gives up and returns a PermError, which counts as an authentication failure. Your legitimate invoice lands in spam and nobody tells you. The SPF Flattening tool exists to get you back under that ceiling.
Why the 10-lookup limit catches so many people
The cap isn't arbitrary. Without it, an attacker could publish an SPF record stuffed with hundreds of nested domains and force any server evaluating it into a recursive query storm, essentially a denial-of-service. Ten lookups is the compromise.
The trouble is how vendors build their records. Adding one provider rarely costs one lookup. Walk through Salesforce:
- You add
include:_spf.salesforce.com. That's 1 lookup. - That domain contains three nested includes of its own:
include:spf1.salesforce.com,include:spf2.salesforce.com,include:spf3.salesforce.com. That's 3 more. - Each of those may reference additional
aormxmechanisms, adding still more.
So a single vendor can quietly cost you four or five lookups. Stack two or three of them and you're at 12 or 15 without realizing it. And the failure is silent from your side. You send the message believing it went out; the recipient's server counted 11 lookups, threw a PermError, and dropped it in junk. You find out weeks later, if at all.
What flattening actually does
Flattening trades indirect references for direct ones. Instead of include:, a, and mx mechanisms that each cost a lookup, you publish the raw IP ranges behind them using ip4: and ip6:.
The difference matters because IP mechanisms are free. When a receiving server hits ip4:192.0.2.1 or ip4:198.51.100.0/24, it does zero DNS lookups. It just compares the sending IP against the list it already has in hand. Replace your vendor includes with their underlying IPs and your lookup count drops to one or zero, and the PermError risk goes away.
A flattening tool does the recursive work for you:
- Parse and enumerate. It reads your SPF record and finds every
include:,a,mx, andptrmechanism. - Resolve deeply. It follows each nested reference all the way down, resolving every hostname to its IPv4 and IPv6 blocks.
- Deduplicate and aggregate. It strips duplicate IPs and collapses adjacent ranges into tidy CIDR blocks.
- Emit clean output. It hands back a valid SPF string built only from
ip4:andip6:directives.
If you'd rather build a record from scratch or check what you already have, the SPF Generator and the SPF record lookup cover those jobs.
The catch: IP drift
Flattening isn't free of downsides, and the main one is drift. Cloud providers expand and reshuffle their infrastructure constantly, and they update their own SPF records when they do. If you keep include:_spf.google.com, you inherit Google's changes automatically because the lookup happens live at evaluation time.
Flatten that into static IPs and your record becomes a snapshot. If Google adds a new sending range six months from now and mails from it, your frozen record won't have it, and that legitimate mail fails SPF. You've traded one silent failure for a different one.
So don't flatten and forget. Treat it as maintenance: re-run the tool on a schedule, weekly or whenever you onboard a new platform, so your IP lists stay in step with the vendors. A reasonable middle ground is to keep live include: mechanisms for your one or two primary providers, where you want automatic updates most, and only flatten the sprawling, deeply nested secondary tools that are actually pushing you over the limit.
A launch that nearly didn't happen
Picture a growing e-commerce shop gearing up for Black Friday. They've wired in an email delivery platform, a review service, a transactional receipt engine, an analytics tool, and a helpdesk. Each vendor's docs say the same thing: add our include:. They end up with what looks like a modest record:
v=spf1 include:_spf.google.com include:spf.hubspot.com include:mailgun.org include:zendesk.com include:sendgrid.net ~all
Five includes on the page. But the moment the campaign fires, millions of messages bounce with 550 5.7.1 SPF PermError: too many DNS lookups. The nested records behind SendGrid and Mailgun had pushed the real count to 14.
They drop the domain into the flattening tool. It traces all 14 lookups, collapses the chain into CIDR blocks, and returns a flattened record. They update the TXT record, the lookup count falls to 1, and deliverability is back to normal within minutes.
The short version
The 10-lookup limit from RFC 7208 is there for good reasons, but modern SaaS stacks blow past it easily and fail quietly when they do. Flattening deconstructs nested includes into raw, lookup-free IP ranges so your record evaluates cleanly and your mail keeps reaching the inbox.
Just remember it's a living record, not a set-it-and-forget-it fix. Re-run it on a cadence, keep an eye on new senders, and pair it with monitoring, the DMARC Report Parser is a good way to spot authentication failures before your customers do.