The SPF Include Mechanism, Explained Properly
What include: actually does during SPF evaluation, why it's not the same as simply copy-pasting another domain's IP ranges, and where it commonly goes wrong.
What include: Actually Evaluates
The include mechanism is the piece of SPF syntax that lets one domain delegate a portion of its sender authorization to another domain's record, and it's by far the most common mechanism in real-world SPF records because almost nobody sends all of their mail directly from infrastructure they own. When a receiving server hits include:example-vendor.com during evaluation, it doesn't just note the domain name and move on — it performs a full DNS lookup for example-vendor.com's own SPF TXT record, then evaluates that entire record's mechanisms exactly as if they'd been written inline, purely to determine whether the current sending IP produces a pass under that vendor's rules.
What comes back from that nested evaluation isn't the vendor record's own qualifier result in a literal sense — it's translated into a simple match or no-match signal for the include mechanism itself. If the included record's evaluation results in a pass for the IP in question, the include mechanism matches. Anything else — fail, softfail, neutral, or even the included record running out without matching — means the include mechanism itself simply doesn't match, and the parent record's evaluation moves on to whatever mechanism comes next.
Syntax and a Worked Example
The syntax is simple: include: followed immediately by a domain name, no spaces, case-insensitive. A typical multi-vendor record looks like this: v=spf1 include:_spf.google.com include:sendgrid.net include:mailgun.org ~all. Reading it left to right, a receiving server checking a message that claims to be from Google Workspace infrastructure will match on the first include and stop there — it never needs to evaluate the SendGrid or Mailgun includes at all for that particular message, since SPF evaluation halts at the first match.
This left-to-right, stop-at-first-match behavior matters more than it looks like it should. Ordering includes roughly by sending volume, with your highest-volume vendor first, has no effect on correctness but can meaningfully reduce the average number of lookups a receiving server performs across your actual mail traffic, since most checks will resolve on an early match rather than working through every include.
Include vs. Static IP Ranges vs. Redirect
| Approach | Stays Accurate Automatically? | Lookup Cost | Best For |
|---|---|---|---|
include: | Yes | 1 + whatever the target record costs | Any vendor that actively maintains and publishes one |
Static ip4:/ip6: | No — needs manual updates | 0 | Infrastructure you fully control, or lookup-constrained records |
redirect= | Yes | 1, replaces rest of record | Fully delegating an entire domain's SPF policy to another, rare outside specific setups |
Common Mistakes With Include
What Breaks When an Include Domain Goes Away
An include is a live pointer, not a snapshot, and that cuts both ways. It's convenient because your record automatically reflects a vendor's current sending infrastructure without you touching DNS. But it also means your SPF evaluation now depends on that vendor's DNS zone continuing to exist and continuing to publish a valid record indefinitely. If a vendor is acquired, rebrands, or simply lets an old SPF include domain lapse, every domain still referencing it in an include mechanism will get a permerror on that mechanism the next time mail is evaluated — not a graceful "no longer applicable," but a hard error condition, because include specifically requires the target to resolve to a valid record.
This is a real, if uncommon, failure mode worth checking for periodically: audit your includes not just for relevance but for whether the target domains still resolve to valid SPF records at all, particularly for less actively maintained third-party tools.
How Include Interacts With the Lookup Limit
Every include mechanism costs at least one lookup against SPF's shared 10-lookup ceiling, and often considerably more once its own nested mechanisms are counted. Because include is simultaneously the most common mechanism in real-world records and the one most likely to hide additional recursive lookups inside it, it's also the single biggest contributor to records that accidentally exceed the limit. A record with what looks like a modest four or five include lines can easily resolve to 9, 10, or more actual lookups once every vendor's own record is expanded and counted.
Why Vendors Ask for an Include Instead of Publishing IPs Directly to You
It's worth understanding the vendor's side of this relationship, because it explains why include exists as a mechanism at all rather than every setup guide just listing IP ranges to paste in. A mail-sending vendor operating at any real scale is constantly adjusting its outbound infrastructure — adding capacity in a new region, rotating out address space that's picked up a bad reputation, migrating to new cloud provider ranges, or splitting traffic across additional pools for deliverability reasons. If every one of their customers had pasted static IPs into their own SPF records, none of those changes could happen without asking thousands of customers to manually update their DNS in lockstep, which is operationally unworkable at scale. Publishing a single SPF record at a stable include hostname and asking customers to reference it with include: turns that coordination problem into something the vendor can solve unilaterally, on their own timeline, without anyone else needing to do anything.
This is also why the include mechanism's "live pointer" behavior, which can feel like a downside when you're trying to reason about exactly what's authorized at any given moment, is actually the entire point of the design. The alternative — static, vendor-provided IP lists — is precisely what flattening produces, and precisely why flattening carries the ongoing maintenance cost it does.
Reading a Real Vendor's Include Chain
Take Google Workspace as a concrete example, since it's one of the most widely referenced includes in real-world records. A typical setup adds include:_spf.google.com to a domain's SPF record. Resolving that hostname's own TXT record doesn't return a flat list of IPs directly; historically it has returned a small number of further include mechanisms pointing at region- or purpose-specific sub-records, each of which is what actually contains the ip4 and ip6 ranges. The practical effect is that one line in your record, include:_spf.google.com, can expand through two layers of nested includes before reaching literal IP ranges — meaning that single include can cost three or four lookups against your budget, not one, once every layer is counted. This is exactly the kind of detail a naive top-level read of your own record will never surface, and exactly why recursive tooling matters more than manual inspection for anything beyond the simplest setups.
What "No Match" From an Include Actually Tells You
It's worth sitting with the earlier point about fail results inside an include a bit longer, because it trips people up in a specific way. Imagine your record is v=spf1 include:vendor-a.com include:vendor-b.com -all, and a message arrives from an IP that vendor-a.com's own record would explicitly mark with a hard fail for. That fail does not propagate upward as your record's overall result. What actually happens is: the include:vendor-a.com mechanism evaluates vendor-a.com's record, finds a hard fail for this IP, and translates that into "this mechanism does not match" — evaluation simply proceeds to include:vendor-b.com next, exactly as if vendor-a.com's record had said nothing about the IP at all. Only if none of your mechanisms match, all the way through, does your record's own trailing -all take over and produce the final fail. In other words, an included record's own qualifiers (pass, fail, softfail, neutral) only ever matter for producing a match or no-match signal for that one include mechanism — they never directly determine your record's ultimate outcome.
Keeping an Include Inventory
For any domain sending mail through more than two or three vendors, it's worth maintaining a simple standing record, outside of DNS itself, of what each include in your SPF record is for: which vendor, which product or team uses it, who owns that relationship, and roughly when it was added. This sounds like overhead for something that's "just DNS," but it's the difference between a five-minute decision when auditing the record later ("yes, still used, keep it") and a much slower, riskier one ("not sure if anything still depends on this, better leave it in just in case") that's exactly how records accumulate permanent bloat in the first place.
What Happens Mechanically Inside a Single Include Evaluation
It's worth breaking the include evaluation process into its literal steps, because the sequence explains several behaviors that otherwise seem arbitrary. First, the evaluator takes the domain named after include: and issues a DNS TXT query for it, exactly one query, which is the lookup that counts against your budget. Second, it inspects the returned TXT records and selects the one beginning with v=spf1 — if there isn't exactly one, that's a permerror right there, independent of anything else. Third, it evaluates that selected record's own mechanisms in order, recursively applying the same left-to-right, stop-at-first-match logic your top-level record uses, including recursively counting any further lookups those mechanisms trigger. Fourth, and this is the step people most often get wrong intuitively, it translates whatever qualifier that nested evaluation lands on — pass, fail, softfail, or neutral — into a simple binary signal for the include mechanism itself: pass becomes "this include matches," and everything else becomes "this include does not match, keep going." There is no fifth step where a fail inside the included record propagates outward as anything other than "no match here."
A Common Real-World Failure: The Renamed Vendor Include
One specific include-related failure worth knowing about by name, because it doesn't look like a typical SPF problem when it happens: a mail vendor rebrands, migrates to new infrastructure, or gets acquired, and in the process retires their old SPF include hostname in favor of a new one, sometimes with a transition period, sometimes without. If your record still references the old hostname after that domain stops resolving to a valid SPF record, you don't get a warning — you get a permerror the next time your record is evaluated, because include: specifically requires its target to resolve. What makes this failure mode particularly sneaky is that it can happen entirely without you doing anything, and the resulting bounce messages from receiving mail servers usually just say something generic about SPF failing, rarely pointing specifically at "your include target no longer has a valid record." Diagnosing it requires actually resolving each of your record's include targets directly and confirming each one still returns a valid v=spf1 TXT record, not just assuming that because it worked at setup time it still works now.
Comparing How Three Common Vendors Structure Their Includes
| Vendor Pattern | Typical Structure | Approximate Lookup Cost |
|---|---|---|
| Flat single-layer vendor | One include resolving directly to a list of ip4/ip6 ranges | 1 lookup |
| Regionally split vendor | One include resolving to 2–3 further region-specific includes | 3–4 lookups |
| Product-line split vendor | One include resolving to several includes, one per product line (transactional, marketing, etc.) | 4+ lookups, varies by how many product lines exist |
None of this structure is visible from your own record's text — include:vendor.com looks identical regardless of which pattern the vendor actually uses internally. This is exactly why resolving a new vendor's include chain before adding it, rather than assuming "it's just one include" based on how it reads, is worth the extra few minutes.
Include Chains and Debugging Tools
When troubleshooting a record that includes several vendors, a tool that only reports your top-level record's syntax is of limited use, because the actual problem is very often buried two or three layers into one specific vendor's chain. What's actually useful is a tool that walks the entire tree exactly the way a receiving mail server would — following every include recursively, tallying the running lookup count as it goes, and flagging specifically which layer, if any, is contributing a void lookup or a missing record. Without that recursive view, a record that looks clean at the top level can be silently broken two levels deep, and no amount of staring at your own DNS zone will reveal it.
Include and the exp= Explanation Modifier, Together
A detail that rarely comes up until it actually matters: when an included record's own evaluation contributes to an eventual overall fail (via the parent record's trailing all mechanism, not the include itself), any exp= modifier that fires to produce a human-readable explanation is the one defined on whichever record's evaluation actually produced the final all match — typically the top-level record's own exp=, not one buried inside an included vendor's record. This matters for domain owners who've configured a custom rejection explanation: don't expect an included vendor's own exp= text to surface in your own domain's failure messages, since include's translation of the nested result into a simple match/no-match signal, as covered earlier, means the included record's own modifiers largely stay local to that nested evaluation rather than propagating outward.
A Practical Checklist for Adding a New Include
| Step | What to Check |
|---|---|
| 1 | Confirm the exact hostname from the vendor's current official setup documentation, not an old support thread |
| 2 | Resolve the target's TXT record directly and confirm it returns exactly one valid v=spf1 record |
| 3 | Trace whether that record nests further includes, and if so, how many lookups the full chain costs |
| 4 | Recalculate your total record's lookup count with the new include added, before publishing |
| 5 | Check whether the vendor's own documentation flags their IPs as subject to frequent change |
Skipping straight to step 4 without steps 1 through 3 is exactly how records end up with a mechanism that looked fine in isolation but pushed the total over budget once its full nested cost was actually accounted for.
Include Chains That Reference Each Other Indirectly
A less common but real pattern worth knowing about: two vendors you've both separately added as includes might, unknown to you, each reference a shared third-party infrastructure provider through their own nested includes. This doesn't cause any error — SPF has no problem evaluating the same underlying IP ranges twice through two different paths — but it does mean your effective lookup cost includes some redundancy that isn't obvious from looking at your own two top-level include lines. Discovering this kind of overlap generally requires actually tracing both chains fully rather than assuming two unrelated-looking vendor names mean two entirely independent lookup paths.
How Case Sensitivity Rules Apply to Include Domain Names
DNS domain names are case-insensitive by design, and this extends cleanly to how include: mechanisms are written and evaluated — include:Vendor.COM and include:vendor.com resolve identically, querying the same underlying DNS record regardless of capitalization used in the SPF record's text. This is worth knowing mainly because it means capitalization inconsistency across a record, while stylistically messy, never causes an actual functional problem the way a genuine typo in the domain name itself would. It's a good practice to keep casing consistent for readability during audits, but it's a cosmetic concern, not a correctness one.
Include and Subdomain-Specific Vendor Records
Some vendors publish their SPF include record not at their apex domain but under a dedicated subdomain specifically reserved for this purpose, such as spf.vendor.com or _spf.vendor.com rather than vendor.com directly. This is generally a deliberate choice on the vendor's part, keeping their SPF-specific DNS record cleanly separated from whatever else lives at their main apex domain, and functions identically to an apex-domain include from the perspective of your own record — you simply reference whatever exact hostname the vendor's documentation specifies, subdomain or not, and evaluation proceeds exactly the same way regardless of which part of the vendor's domain structure the record happens to live under.
A Deeper Look at What "Selecting the Correct Record" Means at Each Domain
When include: queries a target domain for its SPF record, that domain's DNS zone may return multiple TXT records at the queried hostname for entirely unrelated reasons — domain verification strings for various third-party services, DKIM records if the query happens to overlap with an unusual zone structure, or other TXT-based configuration entirely unrelated to SPF. RFC 7208 is explicit that exactly one of the returned TXT records must begin with the literal string v=spf1 for evaluation to proceed; if zero records match that prefix, or if more than one does, the result is a permerror, precisely mirroring the same "must have exactly one SPF record" rule that applies to a domain's own top-level record. This means a vendor's include target domain is just as susceptible to the multiple-SPF-records failure mode as any other domain would be, and it's specifically the vendor's own responsibility to keep their include-target zone clean of duplicate v=spf1 entries — a responsibility outside your direct control, but worth knowing about as one more category of failure that traces back to the vendor's side rather than your own configuration.
Include Chains and the Practical Limits of Manual Auditing
Beyond a certain chain depth, manually tracing an include's full nested structure by hand becomes genuinely impractical, not just tedious. A chain three or four levels deep, where each level might branch into two or three further includes, can represent dozens of individual DNS records that would all need to be manually queried, read, and cross-referenced to build a complete picture by hand. This is precisely the point at which automated recursive tooling stops being a convenience and becomes a practical necessity — not because the underlying logic is complex to understand conceptually, but because correctly executing that logic across a genuinely deep, branching chain, entirely by hand, introduces enough opportunity for simple human error (missing one nested include, miscounting one branch) that the manual result can't be trusted with the same confidence as a tool that mechanically walks the entire tree without skipping anything.
How Include Behaves When the Same Domain Appears More Than Once in a Chain
An edge case worth understanding: if the same domain ends up being referenced more than once within a single record's full recursive expansion — for instance, because two different top-level includes both happen to nest down into a shared third domain somewhere in their own chains — each occurrence is still evaluated as its own separate lookup by a strictly compliant implementation, since SPF evaluation doesn't inherently deduplicate or cache results across different branches of the same overall evaluation tree. This means a shared dependency reached via two different paths genuinely does cost two lookups, not one, even though the underlying DNS record being queried is identical both times. Being aware of this is useful specifically when trying to understand why a record's actual lookup count came out higher than expected based on a mental model that assumed shared dependencies would somehow be counted only once.
Related Reading in This Series
For the full mechanics of why SPF caps total lookups and how to count them, see SPF 10 Lookup Limit. For how include compares to fully delegating your record with redirect, read SPF Redirect Explained. If your record already has too many includes, SPF Record Optimization covers the trimming process directly. To see exactly what your current includes resolve to, open SPF Lookup.
ToolsNovaHub tools are built and independently maintained with a focus on accurate, no-signup network and security utilities. Spotted an error? Let us know.
📋 Related Tools & Guides Comparison
| Resource | Type | Link |
|---|---|---|
| SPF Lookup | Tool | Open Tool → |
| SPF 10 Lookup Limit | Guide | Read Guide → |
| SPF Redirect Explained | Guide | Read Guide → |
| SPF Record Optimization | Guide | Read Guide → |
| DKIM Lookup | Tool | Open Tool → |