Services
AIManaged ServicesConsultingOutsourcing
Differentiators
Compliance & SovereigntyEmail InfrastructureObservability & AIFree Tools & Assessments
Language
EnglishDeutsch — soonFrançais — soonEspañol — soon
Book a review
Field notes · Deliverability

SPF in 2026: the ten-lookup budget, the record that silently breaks, and how to build one that doesn't.

SPF looks like the simplest of the three authentication records and causes the most invisible failures, because it carries a budget most people never count: ten DNS lookups, spent by mechanisms you cannot see from the record itself. Here is how evaluation really works, why a record that reads correctly still returns permerror, the honest trade-offs of every fix, and a generator that keeps count as you build.

SPF, the Sender Policy Framework, is a DNS TXT record that lists the servers authorised to send mail for your domain, and receiving servers check it on every message. Defined by RFC 7208, it carries two hard limits that decide whether it works at all: an evaluation may perform at most ten DNS lookups, and at most two of those may come back empty. Cross either line and the receiver returns permerror, a permanent failure that invalidates your SPF and, by cascade, threatens your DMARC, because DMARC needs an aligned SPF or DKIM pass and a permerror removes the SPF path entirely. The trap is that the budget is spent invisibly: include, a, mx, redirect and exists mechanisms each consume lookups, nested includes consume more, and a record that reads correctly line by line can be over the limit because of what hides inside the includes. Building a durable SPF record means counting what each mechanism costs, structuring streams across subdomains so no single record carries everything, and maintaining the result, because your providers' records change underneath you whether you watch them or not.

How SPF evaluation really works.

The record is the visible part. The evaluation, the part that decides pass or fail, follows rules that explain everything strange about SPF behaviour.

When your message arrives, the receiving server takes the domain from the envelope sender, the Return-Path used in the SMTP transaction rather than the From header a human reads, and queries DNS for that domain's TXT records, looking for one that starts with v=spf1. It then walks the record left to right, mechanism by mechanism, asking of each: does the connecting IP match? An ip4 or ip6 mechanism is checked directly against the address. An a or mx mechanism requires resolving hostnames first. An include hands evaluation to another domain's SPF record and recurses into it. The first mechanism that matches decides the result with its qualifier, pass by default, and if nothing matches, the trailing all mechanism delivers the verdict: softfail for ~all, hardfail for -all.

Two details of that walk carry most of the operational weight. The first is that evaluation happens on the envelope sender's domain, which is why a sending platform using its own bounce domain passes SPF on that domain and not on yours, the alignment problem that surfaces again under DMARC. The second is that the walk costs DNS queries, and the specification meters them: the receiver doing the evaluating is spending its own bandwidth and resolver capacity on your record, so RFC 7208 caps the spend to keep a single check from triggering an unbounded chain of queries. That cap is the ten-lookup budget, and everything in the rest of this note follows from the fact that it is a hard ceiling enforced by the receiver, not a guideline you can quietly exceed.

The ten-lookup budget, mechanism by mechanism.

What makes the limit dangerous is that the costs are uneven and partly hidden. Knowing the price of each mechanism is most of the defence.

The mechanisms that consume lookups are include, a, mx, redirect and exists; ip4 and ip6 consume none, and neither do all or the modifiers. The costs compound in two ways that surprise people. An mx mechanism is not one lookup: it first queries for the domain's MX hosts and then resolves each host returned, so a domain with three mail servers can spend four lookups on that single innocent-looking mechanism. And an include is a door, not a line: the receiver recurses into the included domain's record, and every lookup-consuming mechanism inside counts against your budget, including further nested includes. A marketing platform's include that contains three more includes of its own just spent four of your ten, and nothing in your record's text shows it.

The ptr mechanism deserves its own sentence: it performs reverse-DNS work for the connecting IP, is slow and unreliable, costs lookups, and the SPF specification itself recommends against it. If a record you have inherited contains ptr, removing it is the first change to make. The practical arithmetic of the budget is sobering once nested includes are counted. Typical sending services cost two to four lookups each, so a stack of three or four providers, the workspace mail, a campaign platform, a transactional sender, a CRM that sends notifications, can put a record at nine to twelve lookups. That is why the limit is rarely hit at setup, when the record holds one or two services, and almost always at growth, on the day someone adds the next tool and the count crosses ten without anyone touching the existing lines.

what each mechanism costs
SPF mechanisms and their DNS lookup cost.
MechanismDNS lookupsNote
ip4 / ip60Free — flattened records are built from these
include1 + everything nested insideThe hidden spender
a1Per hostname resolved
mx1 + 1 per mail host3 MX hosts ≈ 4 lookups
redirect / exists1 eachCount them like include
ptrseveral, unboundedDiscouraged by the spec — remove
all0The verdict, not a query

The other limit: two void lookups.

Almost every guide stops at the ten-lookup rule. RFC 7208 carries a second, stricter limit that breaks records which are nowhere near ten.

A void lookup is a DNS query in your evaluation that returns nothing useful: an NXDOMAIN because the name does not exist, or an empty answer because the record is gone. The specification allows two void lookups per evaluation, and the third produces permerror regardless of your total count, so a record spending only six of its ten lookups can still fail outright if three of those queries come back empty. The usual culprits are mundane: an include pointing at a service you cancelled two years ago whose SPF record has since been removed, a typo in a hostname that never resolved, a vendor that restructured its DNS and retired the name your record still references.

The void limit changes how you should think about stale entries. A dead include is not merely wasted budget sitting harmlessly in the record; it is an independent failure mode, two of which are tolerated and the third of which takes your authentication down. That makes pruning a correctness exercise rather than housekeeping: when a sending service is decommissioned, its include comes out of the record the same week, and a periodic check that every name in the record still resolves is part of basic maintenance. It also explains a class of mystery failures, the record that worked for years and broke without being edited, where the edit happened on the vendor's side, a name your record references quietly ceasing to exist, and the void counter did the rest.

Build your record, with the meter running.

Pick your senders and add any custom entries, and this assembles the record while counting the lookup budget the way a receiver will, nested includes included. The counts for named providers are typical values; verify against the provider's current documentation when you publish.

Common senders (typical lookup cost)
0 / 10 lookups
Publish as TXT at example.com Record value

The meter uses typical nested-lookup costs per provider; the true count depends on each provider's record on the day a receiver evaluates it, which is why the monthly re-count matters. One record per domain, every name in it alive, and if the meter runs hot, the durable fix is a subdomain per stream, not a squeeze.

Permerror, and how it cascades into DMARC.

The failure mode deserves its own section, because the damage does not stop at SPF and the symptoms rarely point at the cause.

When evaluation breaches either limit, the receiver returns permerror, a permanent error meaning the record cannot be evaluated, and treats your SPF as invalid. Different receivers respond differently, rejection, spam-foldering, or silently weighting against you, and Microsoft has historically been among the stricter, blocking sender domains that fail authentication. What makes permerror uniquely frustrating is its invisibility from the sending side: the record looks right, the mail flowed yesterday, and the breach happened because a provider added a nested include or a stale name went void, neither of which shows in your zone file. The first place it becomes visible is usually your DMARC aggregate reports, where a source that used to show SPF pass starts showing permerror, which is one more reason those reports are worth reading weekly.

The cascade is the expensive part. DMARC passes when SPF or DKIM passes and aligns with the From domain, so a permerror removes the entire SPF path from that calculation, and every message that was relying on SPF alignment, rather than DKIM, fails DMARC outright. If your DMARC policy has reached quarantine or reject, the policy you built to stop attackers now quarantines or rejects your own legitimate mail, and a lookup-count drift becomes a customer-visible outage. The defence is redundancy: aim for every legitimate sender to have both an aligned SPF pass and an aligned DKIM signature, so that the failure of either leg, a permerror on one side, a broken signature on the other, leaves DMARC standing on the remaining one. Mail that passes on a single leg is one configuration change away from failing, and the configuration change is frequently not yours.

The fixes, in the order to try them.

There are four real responses to a record over budget, and they are not interchangeable. The honest guide is the order, because the popular fix is the one with the most debt attached.

Prune first, always. Most over-budget records carry includes for services the organisation stopped using, an mx mechanism nobody questioned, or a ptr that should never have been there, and removing what is dead frequently brings the count under ten with no structural change at all. Pruning also pays the void-lookup debt, since the stale entries most likely to go void are exactly the ones pruning removes. If your mail hosts are stable, replacing mx with their literal ip4 addresses trades a multi-lookup mechanism for free ones. Only when a pruned, honest record is still over the limit is a structural fix warranted, and at that point the question is which one matches your situation.

Subdomains are the durable answer for most senders, and the logic comes straight from how evaluation works: SPF is checked on the envelope sender's domain, every subdomain is its own domain for that purpose, and each brings a fresh ten-lookup budget. Moving the marketing platform's include to news.example.com and the transactional sender's to mail.example.com leaves the root record holding only the workspace provider, with room to spare everywhere, and it compounds with the reputation isolation that a stream-per-subdomain layout buys you anyway. Flattening, replacing every lookup-consuming mechanism with its resolved ip4 and ip6 addresses, produces a zero-lookup record and a maintenance debt: providers change their IP ranges without notice, a flattened record is a snapshot that rots, and the failure mode is legitimate mail bouncing months later for reasons nobody connects to the flatten. If you flatten, automate the refresh or use a managed service that does; a hand-flattened record edited once and forgotten is a scheduled outage. Macro-based SPF, the fourth route, keeps evaluation to a couple of lookups by resolving authorisation dynamically per check, and suits teams comfortable operating the DNS machinery it requires. The summary that fits on a sticky note: prune, then subdomains, then automated flattening or macros, and never flatten by hand what you are not committed to maintaining.

over budget? the fix ladder
Fixes for an SPF record over the lookup limit, in recommended order.
FixWhat it doesThe catch
1 · PruneRemove dead includes, ptr, needless mxNone — do this regardless
2 · SubdomainsEach stream gets its own 10-lookup budgetPer-subdomain setup and warmup
3 · FlatteningPure ip4/ip6 record, zero lookupsRots as provider IPs change — automate it
4 · MacrosDynamic resolution in ~2 lookupsDNS machinery to operate

A diagnosis, start to finish.

The mechanics are abstract until the day your mail starts bouncing. This is the walkthrough we run when a sender arrives with "SPF was fine and now it isn't", in the order that finds the cause fastest.

Start where the evidence is, the DMARC aggregate reports. Filter for your known, legitimate sending sources and look at their SPF column over the past few weeks. A source that flipped from pass to permerror dates the breach for you, and the date is a clue: cross-reference it against changes to your stack, a tool added, a vendor migrated, and against nothing at all, because a permerror with no change on your side points at a provider editing its own record. If the reports show fail rather than permerror, the problem is different, usually alignment or an unauthorised source, and the lookup budget is not your suspect. Permerror is specifically the record-cannot-be-evaluated signal, and it has exactly two structural causes worth checking in order.

Count first. Resolve your record the way a receiver does, walking into every include and tallying each include, a, mx, redirect and exists encountered, nested levels included. If the total is eleven or more, you have your cause, and the date of the breach is the date some nested record grew. If the count is under ten, check the voids: query every hostname your record references, directly and inside includes, and note any that return NXDOMAIN or an empty answer. Three or more voids is your cause, and the dead name tells you which vendor relationship rotted. If both counts are clean, fall back to the disqualifiers, two SPF records published on the domain, a syntax error introduced by a recent edit, a record over the string-length limit truncated by a DNS tool, each of which produces the same permerror with a different fingerprint. The fix follows the cause: prune or restructure for a count breach, remove the dead reference for a void breach, merge or repair for a disqualifier. What the walkthrough never includes is guessing, because every cause on the list is checkable in minutes, and the sender who checks in order fixes in one pass what the sender who guesses fixes in four.

The last step is the one that prevents the next incident: write down what the count was when you finished. A record at six lookups has room for one more service; a record at nine does not, and knowing which one you are holding turns the next "can we add this tool?" from a gamble into a planning question. The senders who get burned twice are the ones who fixed the count without recording it, added a tool eight months later, and re-ran the same outage with a different vendor's name in the include. The number is the asset; the record is just where it lives. A one-line entry in your infrastructure notes, the date, the count, and which includes spend what, costs thirty seconds to write and turns every future stack decision into arithmetic. It is the cheapest piece of documentation in email operations, and the one whose absence is felt at the worst possible moment, mid-outage, with a vendor on the line asking what your current count is and nobody able to answer.

Passing is not aligning: the Return-Path problem.

A record can be perfect, every check green, and still contribute nothing to your DMARC. The gap is alignment, and it lives in a header most people never look at.

SPF is evaluated against the envelope sender, the Return-Path address used in the SMTP transaction, not the From header your recipient sees. Sending platforms routinely set the Return-Path to their own bounce domain so they can process your bounces, and when they do, SPF is checked, and passes, on the platform's domain. For raw SPF that is a pass; for DMARC it is a miss, because DMARC's SPF leg requires the passing domain to align with your From domain, and the platform's bounce domain does not. The result is the confusing-but-common report line: SPF pass, DMARC SPF alignment fail, on mail that is entirely legitimate.

The fix is the custom Return-Path most serious platforms offer, a bounce subdomain on your own domain, bounce.news.example.com, that the platform manages but that shares your organisational domain, so the SPF pass aligns under DMARC's relaxed mode. Configuring it per sending platform, alongside that platform's DKIM signing on your domain, is what turns each sender into the two-legged, fully aligned source that survives a failure on either leg. This is the same seam the subdomain strategy note works from the architecture side and the DMARC journey works from the policy side; SPF is where the seam is physically stitched, one Return-Path at a time, and it is the detail an authentication audit checks first because it is the one most often missed.

The 255-character mechanics, and the record type that refuses to die.

Two pieces of DNS plumbing sit underneath SPF, and both produce failures that look mystifying until you know the plumbing is there.

A DNS TXT record is built from character strings, and each string is capped at 255 characters. A record longer than that is not forbidden; it is split into multiple quoted strings inside the same TXT record, which receivers concatenate back together before evaluating. The failure happens at the tooling layer: some DNS management panels silently truncate a long paste at 255 characters instead of splitting it, and the published record ends mid-mechanism, syntactically broken, returning permerror on every evaluation. A flattened record is the usual victim, because replacing includes with literal IP addresses inflates the length well past the limit. The check is simple, query the published record and compare it character-for-character with what you intended to publish, and the fix is publishing it as properly split strings or, better, keeping the record short enough through subdomain structure that the limit never comes into play. There is also a practical ceiling above the per-string rule: a record assembled from many strings still has to fit in DNS responses comfortably, and a record approaching that size is a record asking to be restructured rather than extended.

The second piece of plumbing is historical. Early SPF deployments used a dedicated DNS record type, type 99, literally named SPF, alongside TXT. The experiment failed, resolver and tooling support never materialised, and RFC 7208 deprecated the SPF type definitively: the standard is TXT, full stop. The type still matters only because it lingers, in old zone files migrated forward for a decade, in documentation written before 2014, and in the occasional DNS panel that still offers it in a dropdown. A domain publishing its policy only in a type-99 record is publishing it nowhere, since receivers do not query it, and a domain publishing both types invites the two records to drift apart until the one being read no longer matches the one being edited. The audit step costs one query: confirm the policy lives in a TXT record, confirm no type-99 fossil survives beside it, and delete the fossil if it does. Plumbing problems are the cheapest class of SPF failure to fix and the most expensive to find, because nothing in the record's visible content hints that the container, not the contents, is what broke.

The rules that disqualify a record before counting starts.

Before the lookup budget even applies, a handful of structural rules can invalidate a record outright, and they are violated innocently all the time.

One record per domain, exactly. Publishing two SPF records, which happens when a second team adds one for a new tool without noticing the first, is itself a permerror that invalidates both; every authorised service belongs in the single record as another mechanism. The record must begin with v=spf1, must be a TXT record rather than the long-deprecated SPF type, and must respect DNS string-length mechanics, with long records split into properly concatenated strings rather than truncated. The all mechanism belongs at the end, and its qualifier is your default verdict: ~all softfails unknown senders, -all hardfails them, and the two entries that should never appear are +all, which authorises the entire internet and is materially worse than publishing nothing, and ?all, which expresses no opinion and wastes the record. In a DMARC world the softfail-versus-hardfail debate matters less than it used to, since DMARC's policy does the enforcing either way, and ~all is the safer operating posture while your sending map is still being discovered.

Non-sending domains get the shortest record of all and need it most: v=spf1 -all, the explicit declaration that no server sends for this domain. Parked domains, defensive registrations and retired brands without it are standing invitations, since a domain with no SPF at all gives receivers nothing to check a spoof against. One line, paired with a restrictive DMARC record, and the domain is closed, which for an operator holding a portfolio of domains is among the cheapest security wins available, the same close-the-unused-doors discipline the DMARC note applies with sp and parked-domain records.

Maintenance: the record changes even when you don't touch it.

The defining property of SPF in production is that its effective content is partly outside your control, which makes maintenance a calendar item rather than a reaction.

Your record's true lookup count is computed at evaluation time from the live state of every included domain, so it changes when your providers change theirs: a vendor adds a nested include and your count rises; a vendor retires a hostname and your void counter starts ticking. Nothing in your zone file moved, and your authentication drifted anyway. The discipline that matches this reality is a monthly re-count, two minutes with a counting tool or the generator above, plus a standing rule that stack changes touch the record the same week, services added and, just as importantly, removed. The DMARC aggregate reports close the loop: they show SPF results per source, so a source flipping from pass to permerror appears there before most senders would otherwise notice, and reading them weekly turns a silent drift into a Tuesday fix.

On a self-hosted stack the same discipline applies with more of it under your own roof. An operator running PowerMTA controls the envelope sender per virtual MTA, so Return-Path alignment is a configuration choice rather than a vendor feature; the sending IPs are yours, so the root record can be lean ip4 entries that cost nothing; and per-stream subdomains map naturally onto per-VMTA identities, each with its own small record and full budget. The failure modes do not disappear, a stale include is stale on any stack, but the surface area shrinks to what you operate, and the monitoring is your own logs and reports rather than a dashboard's summary. That is the lens we bring, because we run sending infrastructure and read these records professionally, and SPF is the layer where five minutes of counting prevents the outage that takes a day to diagnose. Where it helps to have the whole authentication stack, SPF, DKIM, DMARC and the Return-Paths that stitch them, audited and maintained by people who do it for a living, that is the work our deliverability audit exists to do, with the ongoing care running through our email infrastructure and deliverability practice. The foundations connect across the cluster: the bulk sender requirements make SPF mandatory, and the subdomain and DMARC notes carry the same architecture forward.

Questions senders are asking in 2026.

What is an SPF record and what does it do?
SPF, the Sender Policy Framework, is a DNS TXT record on your domain that lists the servers allowed to send mail for it. When a message arrives, the receiving server looks up the SPF record of the envelope sender's domain and checks whether the connecting IP is on the list. It is one of the three authentication pillars alongside DKIM and DMARC: SPF says which machines may send, DKIM proves the message was not altered, and DMARC ties both to the From address your recipient sees and tells receivers what to do on failure.
What is the SPF 10 DNS lookup limit?
RFC 7208, the SPF specification, caps evaluation at ten DNS lookups per check. Every include, a, mx, redirect and exists mechanism in your record consumes lookups, and nested includes count too, so a record that lists several sending services can quietly exceed ten. The limit exists to protect receiving servers from unbounded query chains, and it is a hard ceiling: lookup eleven does not happen, and the evaluation returns a permanent error instead of a result.
What happens when my SPF record exceeds 10 lookups?
The receiver returns permerror, a permanent evaluation failure, and treats your SPF as invalid. Your legitimate mail can be rejected, spam-foldered or silently dropped, and because DMARC needs an aligned SPF or DKIM pass, a permerror removes one of your two paths to a DMARC pass. The cruellest part is that nothing looks wrong: the record reads correctly line by line, the mail worked last month, and the failure only appears when the lookup count crosses the line, usually after someone adds one more sending service.
Which SPF mechanisms count toward the lookup limit?
include, a, mx, redirect and exists each consume DNS lookups; ip4 and ip6 consume none, which is why flattened records are built from them. Two of these are worse than they look: mx first queries for the MX hosts and then resolves each one, so a domain with three mail servers can spend four lookups on a single mx mechanism, and an include that contains its own nested includes spends one lookup per level. The ptr mechanism also costs lookups and is explicitly discouraged by the specification itself, slow, unreliable, and best removed on sight.
What is a void lookup and why does it matter?
A void lookup is a DNS query in your SPF evaluation that returns nothing, an NXDOMAIN or an empty answer, usually from a stale include pointing at a service you stopped using or a typo in a hostname. RFC 7208 allows only two void lookups per evaluation; a third returns permerror even if your total lookup count is comfortably under ten. It is the limit almost nobody knows about, and it means a dead include is worse than wasted budget: an independent way to break your record.
How do I count the DNS lookups in my SPF record?
Walk the record mechanism by mechanism: each include, a, mx, redirect and exists is at least one lookup, then resolve each include and count the lookup-consuming mechanisms inside it, recursively, because nesting counts against the same budget. An mx mechanism costs one lookup plus one per mail host returned. ip4, ip6, all and the modifiers cost nothing. The generator on this page does the bookkeeping for common providers as you build, and a monthly re-count is worth the two minutes, because your providers' records change underneath you.
How many sending services fit inside the SPF limit?
Fewer than people expect. Typical providers cost two to four lookups each once their nested includes are counted, so three or four services, a workspace provider, a marketing platform, a transactional sender, a CRM, can already put a record at nine to twelve lookups. That is why the limit tends to be hit not at setup but at growth, the day the fourth or fifth tool is added, and why the durable fixes are structural, splitting streams across subdomains, rather than squeezing one more include into a single record.
What is SPF flattening and should I use it?
Flattening replaces every lookup-consuming mechanism with the IP addresses it resolves to, producing a record of pure ip4 and ip6 entries that costs zero lookups. It works, and it carries a real maintenance debt: providers change their IP ranges without telling you, and a flattened record is a snapshot that silently rots, failing legitimate mail when a provider moves. If you flatten, automate the refresh or use a managed flattening service rather than doing it by hand once and forgetting it. And if you are under the limit, do not flatten at all; prune instead.
Are there better fixes than flattening?
Usually, yes, and the right one depends on the problem. First prune: remove includes for services you no longer use, drop ptr, and replace mx with the actual IPs if your mail hosts are stable. If you are structurally over the limit, move streams to subdomains, since SPF is evaluated on the envelope sender's domain, each subdomain brings its own fresh ten-lookup budget, and your marketing platform's include can live on news.yourdomain.com without crowding the root. Macro-based SPF services are the third route, keeping evaluation to a couple of lookups by resolving the authorisation dynamically.
Does SPF alignment matter as much as SPF passing?
For DMARC, yes, and the distinction trips people constantly. SPF is checked against the envelope sender, the Return-Path, not the From header your recipient sees. A sending platform that uses its own bounce domain can pass SPF on that domain while your DMARC still fails SPF alignment, because the passing domain does not match your From domain. The fix is a custom Return-Path on your own domain or subdomain, or leaning on DKIM alignment instead. A green SPF check is necessary but not sufficient; alignment is what DMARC scores.
Should my SPF record end with ~all or -all?
The all mechanism is your default verdict for any server not matched earlier. ~all is a softfail, telling receivers that unlisted servers are probably not legitimate, and -all is a hardfail, telling them they definitely are not. In a DMARC world the practical difference has narrowed, because DMARC's policy does the enforcing, and ~all is the safer operating choice for most senders while the sending map is in motion. What you should never run is +all, which authorises the entire internet and is worse than no record, or ?all, which says nothing at all.
Can I have two SPF records on one domain?
No. A domain must publish exactly one SPF record, and two or more is itself a permerror that invalidates them all. This happens innocently: one team adds a record for a new tool without noticing the existing one. If you need to authorise several services, they all go in one record as multiple mechanisms, and if that record then breaches the lookup limit, the answer is the structural fixes, pruning, subdomains, macros, not a second record.
Does SPF survive email forwarding?
Mostly, no. When a message is forwarded, the forwarding server becomes the connecting IP, and that server is not in your SPF record, so SPF fails on the forwarded copy even though the mail is legitimate. This is a known, structural weakness of SPF, and it is the main reason DKIM, whose signature survives forwarding, is the sturdier leg for DMARC alignment, and why ARC exists to carry authentication results across forwarding hops. Build SPF correctly, and plan for the fact that it breaks in transit in ways DKIM does not.
How does a broken SPF record affect DMARC?
Directly and by cascade. DMARC passes when SPF or DKIM passes and aligns with the From domain, so an SPF permerror removes the SPF path entirely, and any sender that was relying on SPF alignment rather than DKIM starts failing DMARC outright. If your DMARC policy is at quarantine or reject, that means your own legitimate mail gets quarantined or rejected, which is exactly how a lookup-limit breach turns into a visible outage. Mail that depends on one aligned path is one configuration drift away from failing; aim to have both.
How should I structure SPF across subdomains?
Each sending subdomain publishes its own SPF record, because SPF does not fall back to the parent: a receiver checking mail.example.com looks only at mail.example.com. That independence is the opportunity, since every subdomain gets its own ten-lookup budget, and a stream-per-subdomain layout, transactional on one, marketing on another, gives each stream's providers room without crowding a single record. The root domain's record then stays small, often just your workspace provider and -all, which is both cleaner and safer.
What SPF record should a non-sending domain publish?
v=spf1 -all, the empty record that authorises no one. A parked or defensive domain that sends no mail should say so explicitly, because a domain with no SPF record at all is easier to spoof. Pair it with a restrictive DMARC record and the domain is closed: any message claiming to come from it fails authentication everywhere. It costs one DNS entry and removes a standing impersonation risk on every domain you own but do not use.
How often should I review my SPF record?
Monthly, and on every change to your sending stack. Provider records change underneath you, nested includes grow, services get adopted and abandoned, and both the ten-lookup count and the two-void-lookup count drift over time. A monthly re-count plus a check of your DMARC aggregate reports, which show SPF results per source, catches the drift before it becomes a permerror. The reports are the early-warning system; the re-count is the maintenance.
Is SPF still necessary if I have DKIM and DMARC?
Yes. The mailbox providers' bulk-sender requirements expect SPF and DKIM both, with DMARC on top, and a sender missing SPF fails that baseline regardless of how clean its DKIM is. Operationally, SPF is also your redundancy: DKIM signatures break when intermediaries modify messages, and on those messages an aligned SPF pass is what keeps DMARC passing. The goal is two independent aligned paths, so that no single breakage, a forwarding hop, a signing misconfiguration, a lookup-limit breach, takes your authentication down alone.
Deliverability audit

Count the lookups before the receivers count them for you.

We audit your SPF against both limits, prune what is dead, restructure streams onto subdomains where the budget demands it, fix the Return-Paths so passes align, and put the monthly re-count on a calendar so the record that works today still works after your providers change theirs. Authentication that fails silently is the most expensive kind; ours is watched.

Ten lookups, two voids, one record Prune before you flatten Aligned on both legs