16 June 2026 · 13 min read
SPF has a hard limit baked into the specification: a receiving server is allowed to perform at most ten DNS lookups while evaluating your record. Cross that line and the result is not a soft warning, it is a permerror, and a permerror means SPF stops authenticating your mail. The frustrating part is that you rarely notice until something breaks, because the count is not visible in your DNS record. It is the sum of every include, every a, every mx, every redirect, and every nested record those mechanisms pull in behind the scenes. You add one new sending service, that service nests three more includes inside its own record, and suddenly a record that read as four lines on screen is doing eleven lookups on the wire.
SPF flattening is the standard answer to this. Instead of telling receivers to go and resolve include:_spf.google.com, you resolve that include yourself, extract the actual IP ranges it authorises, and publish those IPs directly in your record. No nested lookup, no counter incrementing. The catch, and it is a serious one, is that flattening trades a DNS lookup problem for a maintenance problem. Vendors change their IP ranges. When they do, a hand-flattened record silently goes stale and starts failing legitimate mail. Automatic SPF flattening is the version of this that keeps working: it resolves and serves a flattened record on your behalf, and it re-checks the sources on a schedule so that when a vendor moves, your record moves with it.
This article explains how that works end to end, what the ten-lookup limit actually counts, why static flattening is dangerous, and how a hosted, automatically re-checked flattened record stays correct without you thinking about it. If you want the deeper specification background as you read, /blog/rfc-7208-spf-explained covers the rules this all sits on top of.
What the ten-lookup limit actually counts
The limit comes from RFC 7208, the SPF specification, section 4.6.4. It exists to stop SPF evaluation from becoming a denial-of-service amplifier. Without a cap, a maliciously crafted record could force receivers into an enormous tree of DNS queries for a single inbound message. So the spec says: count the lookups, and if evaluation needs more than ten, return permerror.
The mechanisms and modifiers that each cost one lookup are:
includeamxptr(deprecated, but still counted, and you should not be using it)existsredirect
The qualifiers ip4, ip6, and all cost zero lookups, because they are literal values the receiver can evaluate without asking DNS anything. That single fact is the whole basis of flattening: if you can convert a costly include into a set of free ip4 and ip6 ranges, you reduce the count without changing who is authorised.
Two details trip people up constantly.
First, the count is recursive. When you write include:_spf.salesforce.com, that is one lookup, but Salesforce's record then contains its own includes, and each of those counts too. The ten you are budgeting is the total across the entire tree, not the number of mechanisms you typed.
Second, mx is worse than it looks. The mx mechanism counts as one lookup to fetch the MX records, but then each MX hostname returned also needs to be resolved to an address, and those resolutions count against you as well. A domain with five MX hosts can burn a large slice of your budget from a single mx token.
We have a dedicated explainer on the mechanics of the cap at /blog/spf-10-lookup-limit, and the specific failure mode when you exceed it at /blog/fix-spf-too-many-dns-lookups. To see your own current count without doing the arithmetic by hand, run your domain through the SPF checker, which expands the full tree and shows the running total.
Why you hit the limit so easily
The ten-lookup budget sounds generous until you count a realistic stack. Consider a mid-sized company that uses:
- Google Workspace for staff mail (
include:_spf.google.com, which itself nests three more includes, so four lookups) - A marketing platform like Mailchimp (one to two lookups)
- A transactional provider like SendGrid or Postmark (one to two lookups)
- A help-desk or ticketing tool (one lookup)
- A CRM such as Salesforce (two or more lookups)
- The domain's own
mxfor any server-originated mail (one plus the MX host resolutions)
That is already past ten before you have added an invoicing tool or a new newsletter service. This is not an edge case, it is the normal trajectory of any organisation that adds SaaS over time. The record looks tidy because each vendor only asked you to paste in one include, but the resolved tree underneath is enormous.
The honest framing is that the ten-lookup limit is in tension with how modern email is actually sent. Most companies legitimately send through more than ten lookups' worth of services. Something has to give, and the two real options are: remove or consolidate senders, or flatten. Often you do both, but flattening is the lever that lets a genuinely complex sending stack stay within spec.
What flattening does, mechanically
Flattening replaces lookup-incurring mechanisms with the literal IP ranges they resolve to.
Take a record like this:
v=spf1 include:_spf.google.com include:sendgrid.net include:servers.mcsv.net ~all
Each of those three includes pulls in nested records. Resolved fully, that might be a dozen or more lookups. The flattened equivalent expands every include into the actual ip4 and ip6 ranges those services publish, producing something closer to:
v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:108.177.8.0/21 ip4:167.89.0.0/17 ip4:198.37.144.0/20 ... ~all
The authorised set of senders is identical. What changed is that the receiver no longer performs any DNS lookups to evaluate it: every term is a literal IP range, which costs zero against the ten-lookup budget. The semantics of ~all (or -all) at the end are unchanged. Alignment, the thing DMARC actually cares about, is also unchanged, because flattening only touches which IPs are authorised, not which domain is in the envelope. If alignment is new to you, /blog/dmarc-alignment-explained covers how SPF authorisation turns into a DMARC pass.
There is a second constraint flattening has to respect: the 255-character limit on a single DNS TXT string, and the practical limit on total record length. A flattened record full of IP ranges gets long. The fix is to split the value into multiple 255-character strings inside one TXT record, which is valid and which receivers concatenate. If you ever need to do this manually, the DNS record splitter handles the chunking, but with hosted flattening it is handled for you.
The problem with doing it by hand
You can flatten manually right now. Resolve your includes, paste the IPs into your record, done. The trouble starts the day after.
Vendor IP ranges are not stable. Email providers add sending infrastructure, retire old ranges, move regions, and acquire new netblocks. The whole reason they give you an include rather than a list of IPs is so they can change their addresses without telling you. The include is a pointer that always reflects their current truth. The moment you flatten it into static IPs, you have copied their truth at a single point in time and frozen it. You have opted out of the update mechanism they built specifically so this would not break.
The failure is quiet and asymmetric, which is what makes it dangerous:
- If the vendor adds a new IP range and your static record does not include it, mail from that new range fails SPF. If your DMARC policy is at
quarantineorreject, that legitimate mail goes to spam or is rejected outright. Nobody gets an error in your dashboard; the recipients just stop seeing your emails. - If the vendor retires a range and you keep authorising it, you are now authorising IP space that may later be reassigned to someone else, which is a security weakness rather than an outage.
Both directions degrade over time, and the decay is invisible until a customer says "I never got your invoice." This is precisely the trap that makes people distrust flattening. Flattening is not the problem. Static flattening is the problem. The mechanism is sound; freezing it in place is what fails.
How automatic flattening fixes the maintenance problem
Automatic SPF flattening keeps the upside of flattening (zero lookups for the receiver) while removing the downside (a record that goes stale). It does this with two moving parts: a hosted record that is served dynamically, and a scheduled re-check that keeps the contents current.
Hosting the record so it can be updated
The first piece is that the flattened record is not pasted into your zone as a frozen string. Your domain delegates the SPF answer to a record that the platform controls and can update on your behalf. In the DMARC Engine model, this is done by delegating your DNS or by pointing the relevant record at a managed target, so the authoritative answer for your SPF can be regenerated whenever the underlying sources change, without you editing your zone each time.
The point of hosting is not magic, it is write access. Manual flattening fails because the only thing that can update the record is a human who has long since forgotten the record exists. Hosting moves the write to a system that is watching the sources continuously. Our product overview for this is at /products/spf, and the operational detail at /docs/hosted-spf.
Re-resolving the sources on a schedule
The second piece is the re-check loop. The platform keeps the original list of what you actually wanted to authorise: include:_spf.google.com, include:sendgrid.net, your mx, and so on. That intent list, the human-meaningful version, is the source of truth. On a schedule, the platform resolves each of those sources fresh, exactly as a receiving mail server would, and rebuilds the flattened IP set from the current answers.
When a vendor's published ranges are unchanged, the rebuild produces the same record and nothing happens. When a vendor has added, moved, or removed a range, the newly resolved set differs from what is currently published, and the platform updates the served record to match. Because the record is hosted, that update propagates without anyone touching the zone file. The effect is that the flattened record tracks the includes the way the includes themselves track the vendor: automatically, on a delay measured in the re-check interval rather than in however long it takes someone to notice an outage.
This is the difference that matters. A manual flattened record reflects the vendor's IPs as they were the day you flattened. An automatically flattened record reflects the vendor's IPs as they are this re-check cycle. You get the lookup savings of static IPs with the freshness of an include.
What happens when a vendor changes their IPs
It helps to walk a concrete sequence, because the value is entirely in the change case.
- Your intent list authorises a transactional provider via
include:smtp.provider.example. Today that include resolves to twoip4ranges, which the platform has flattened into your served record. - The provider brings a new data centre online and adds a third
ip4range to their publishedinclude. They do this exactly as designed; they expect everyone using theincludeto pick it up automatically. - Anyone with a normal
include:smtp.provider.examplein their zone picks up the third range immediately, because receivers resolve the include live on every message. - Anyone with a manually flattened record does not pick it up. Their record still lists two ranges. Mail from the new data centre fails SPF. If they are at
p=reject, that mail is rejected. They will not know until deliverability complaints arrive. - With automatic flattening, the next scheduled re-check resolves
include:smtp.provider.example, sees three ranges where there were two, regenerates the flattened set to include the third, and updates the served record. The new data centre is authorised before, or shortly after, the provider starts sending from it.
The re-check is what closes the gap between "the vendor changed" and "my record reflects the change." It turns the most dangerous property of static flattening, that it cannot follow the vendor, into a non-issue.
There is a related safety consideration worth naming. A good automatic flattener does not blindly trust every resolution. If a source returns a temperror (a temporary DNS failure) during a re-check, the correct behaviour is to keep the last known good ranges rather than dropping them, because a transient lookup failure should never strip authorised senders out of your record. We cover that specific failure at /blog/fix-spf-temperror. The general principle is that re-checks should only ever update on a confident, successful resolution, and should fail safe by retaining the previous good state otherwise.
Where flattening is the wrong tool
Honesty matters here, so it is worth being clear about flattening's limits.
Flattening is not a way to authorise more senders than you should. If your record is over ten lookups because you have accumulated services you no longer use, the right first move is to remove the dead ones, not to flatten everything and carry the cruft as frozen IPs. Flattening a sender you have stopped using just means you are now authorising stale IP space. Audit the list of senders first. The SPF checker and our broader domain analyzer help you see what is actually in the tree so you can prune before you flatten.
Flattening also does not change alignment or policy. It does not make SPF align where it did not before, it does not fix forwarding (where the path changes and SPF breaks regardless of how your record is built), and it does not substitute for DKIM. SPF and DKIM solve overlapping but distinct problems, and DMARC passes if either aligns. If you are leaning on flattening to rescue SPF for forwarded mail, the real answer is DKIM, covered in /blog/dkim-alignment-explained and at the product level on /products/dkim. For why forwarding breaks SPF specifically, see /blog/forwarding-and-dmarc.
And flattening is not a workaround for a vendor that publishes a genuinely enormous include. If one provider's record alone resolves to a vast list, flattening will produce a very long record. It will still be valid and still be zero lookups, but it is a signal to question whether that vendor's footprint is reasonable.
How this fits the path to enforcement
The reason any of this matters is that you cannot safely move to /blog/dmarc-none-to-reject-safely on a foundation that intermittently fails SPF. At p=none, an SPF permerror or a missing IP range is invisible: nothing is enforced, so nothing is blocked, and the breakage hides in your reports. The instant you move to quarantine or reject, every SPF gap becomes a deliverability incident. A record that drifts past ten lookups, or a static flattened record that has fallen behind a vendor's IPs, is exactly the kind of latent fault that turns an enforcement rollout into an outage.
Automatic flattening removes one whole class of that risk before you tighten policy. It keeps the lookup count permanently at zero for the flattened sources, and it keeps the authorised IPs current, so the SPF half of your authentication is stable while you do the careful work of getting to enforcement. The full sequence we recommend, and automate, is laid out in /docs/the-enforcement-journey, and the monitoring that watches for source changes between re-checks is described in /docs/monitoring-and-change-alerts.
In practice the workflow looks like this:
- Inventory your real senders, the human-meaningful intent list, and confirm each is still in use.
- Hand that list to a hosted SPF record so the platform can resolve and flatten it. See /docs/hosted-spf.
- Let the scheduled re-check track vendor IP changes automatically, retaining last-known-good ranges on transient failures.
- Verify the live result any time with the SPF checker, which will show a lookup count comfortably inside the limit.
- Proceed to enforcement on a stable SPF foundation, with DKIM doing the heavy lifting for forwarded and aligned mail.
The short version
The ten-lookup limit is real, it is recursive, and most modern sending stacks blow past it. Flattening fixes the count by turning costly include mechanisms into free ip4 and ip6 literals, leaving the set of authorised senders and the DMARC alignment behaviour unchanged. The danger of flattening was never the technique, it was doing it once by hand and letting it rot while vendors quietly moved their IPs underneath you. Automatic flattening solves that by hosting the record so it can be rewritten, and re-resolving your sources on a schedule so the flattened IPs track the vendors the same way a live include would, with a fail-safe that keeps the last good state when a lookup temporarily fails.
The result is the property you actually want: a record that sits permanently under ten lookups, that you do not have to remember exists, and that does not turn into a deliverability incident the day a provider adds a data centre. Check where you stand today with the SPF checker, read the deeper background at /blog/how-spf-flattening-works and /blog/spf-flattening-explained, and when you are ready to stop maintaining it by hand, see /products/spf.