# SPF 10 Lookup Limit: Causes, Counting and Fixes

URL: https://email-validation.org/guides/spf-lookup-limit/
Updated: 2026-11-06

> Documentation index: https://email-validation.org/llms.txt

An SPF check may run at most 10 terms that need a DNS query, and every `include` inside an included record counts
too. Go past 10 and the check stops with a permanent error (`permerror`) instead of a pass, so SPF no longer vouches
for your mail and some receivers reject it. The usual cause is one `include` per sending service, each pulling in
more includes of its own. The fixes are to remove what you no longer use, move services to their own subdomains, list
your own servers by IP address, and flatten only with care.

## What counts toward the limit

[RFC 7208, section 4.6.4](https://www.rfc-editor.org/info/rfc7208/) names the terms that make the receiver query DNS
while it evaluates your record, and requires receivers to stop at 10 of them:

| Term | Counts toward 10? |
|---|---|
| `include:` | Yes, plus every lookup inside the included record |
| `a`, `mx` | Yes |
| `ptr` | Yes (and it should not be used at all) |
| `exists:` | Yes |
| `redirect=` | Yes, plus the lookups in the record it points to |
| `ip4:`, `ip6:` | No |
| `all` | No |
| `exp=` | No (its lookup happens later, only to build an error text) |

Two sub-limits sit on top of the main count. An `mx` term counts once, but the mail hosts it returns may lead to at
most 10 address (A or AAAA) lookups, or the term itself returns `permerror`. A `ptr` term has the same cap of 10,
though there the extra names are ignored instead of causing an error. Section 5.5 says `ptr` should not be published
at all: it is slow, less reliable when DNS has errors, and it loads the reverse DNS servers.

### The void lookup limit

The same section adds a smaller limit. A void lookup is a query that returns no records, or a reply that the name
does not exist. Receivers should allow only two and return `permerror` on the third. A stale `a:` or `mx:` term
that points at a retired host name is one way to collect them.

### Other ways to get permerror

The lookup limits are not the only route to a permanent error. RFC 7208 also returns `permerror` when:

- the domain publishes **more than one** record starting with `v=spf1` (section 4.5);
- the record has **a syntax error** anywhere in it (section 4.6), such as an impossible network size like `ip4:192.0.2.0/33`. A typo such as `include=` instead
  of `include:` is worse in a quiet way: it reads as an unknown modifier, which section 6 says receivers must
  ignore, so the include is skipped without any error and that service's mail fails SPF;
- an `include:` points at a domain that has **no SPF record** (the table in section 5.2).

Section 8.7 calls `permerror` a condition that needs the DNS operator to step in. Retrying will not fix it.

## How to count your lookups

Count every lookup term in your own record, then open each `include` and `redirect` target and count its terms too,
all the way down. Take this record:

```text
example.com.  TXT  "v=spf1 ip4:192.0.2.10 include:_spf.mail.example.net include:spf.crm.example.org mx -all"
```

- `ip4:192.0.2.10` costs nothing.
- `include:_spf.mail.example.net` costs 1. If that record holds three more includes, add 3: 4 so far.
- `include:spf.crm.example.org` costs 1, and its record holds only `ip4:` ranges: 5.
- `mx` costs 1: 6 lookups.
- `-all` costs nothing.

Microsoft's [SPF setup guide](https://learn.microsoft.com/en-us/defender-office-365/email-authentication-spf-configure)
gives the same arithmetic: an include whose record holds three more includes adds four lookups in total. It also
explains why an over-limit record can fail only some of the time. Receivers read the record from left to right and
stop at the first match, so mail from a server listed early can still pass, while mail from a server listed late
fails, and the bounce may say the message required too many lookups. A few passing messages prove nothing.

The [SPF checker](https://email-validation.org/tools/spf-checker/) follows every include for you and shows the total number of lookups.

## Why vendor includes add up

Every service that sends as your domain asks for its own `include:`: the mailbox provider, the newsletter tool,
the CRM, the help desk, the billing system. That is what `include` is for: pointing at servers run by independent
organizations (RFC 7208, section 5.2). But each included record is maintained by its vendor and may be a chain of
further includes, so a vendor that adds one to its own record can push you over the limit without any change on
your side.

## How to get back under 10

### Remove includes you no longer use

Start with an inventory of what really sends as your domain. Microsoft recommends a DMARC reporting service for
finding your mail sources ([DMARC aggregate reports](https://email-validation.org/guides/dmarc-aggregate-reports/)). Delete the includes of
services you have stopped using, and `a` or `mx` terms for hosts that no longer send.

Also check which domain each service puts in the envelope sender (the `Return-Path` header of a message it sent).
SPF checks that domain, not the From: address people see. A service that uses its own bounce domain is checked
against its own SPF record, so your include for it never comes into play for that mail. Before you delete such an
include, make sure the service signs with DKIM for your domain, so that DMARC can still pass.

### Move services to subdomains

Give each service you do not run yourself a subdomain, such as `news.example.com` for the newsletter. Each
subdomain has its own SPF record and its own budget of 10 lookups. Microsoft recommends this for bulk mail services,
partly to keep their reputation separate from your staff's mail. The subdomain needs its own record, because the
record of `example.com` does not cover it.

```text
example.com.       TXT  "v=spf1 ip4:192.0.2.10 include:_spf.mail.example.net -all"
news.example.com.  TXT  "v=spf1 include:spf.newsletter.example.org -all"
```

This keeps DMARC working. Under DMARC's default relaxed alignment, a bounce domain like `news.example.com` aligns
with a From: address at `example.com`, because both share the organizational domain
([RFC 9989, section 4.4](https://www.rfc-editor.org/info/rfc9989/)).

### List your own servers by IP address

`ip4:` and `ip6:` cost no lookups. For servers you control, RFC 7208 (section 10.1.1) calls a record that lists the
addresses directly the best option and `mx` the expensive one. Section 10.1.2 names the trade-off: `a` and `mx` let
you renumber hosts without touching SPF, at the cost of a lookup for every receiver. Unless your addresses change
often, list them.

### SPF flattening and its risks

Flattening replaces an `include:` with the IP addresses the included record lists today. The lookups are gone, but
your record now holds a copy nobody updates for you: when the vendor changes its sending addresses, mail from the
new ones fails SPF until you notice.

Microsoft's guide limits flattening to vendors with stable, documented address ranges, and says not to flatten
services whose sending addresses change often, its own Microsoft 365 include among them. If you flatten anyway, it
asks you to document what you replaced and when, watch the vendor's announcements, review the entries at least
quarterly and test SPF after each change.

Flattened records also get long. RFC 7208 (section 3.4) advises keeping the record small enough that the DNS
answer fits in 512 octets, roughly 450 octets for the name and records together, because larger answers can be
silently ignored by some receivers. A single record can be split into several quoted strings of up to 255 octets
each, which receivers join without spaces (section 3.3).

### Let DKIM carry DMARC

DMARC passes when either SPF or DKIM passes for a domain aligned with the From: address
([RFC 9989, section 4.4](https://www.rfc-editor.org/info/rfc9989/)). Mail that is signed with DKIM for your domain
still passes DMARC on a day when SPF breaks, so setting up DKIM for each service is the safer long-term fix. See
[DKIM selectors and key rotation](https://email-validation.org/guides/dkim-selectors-key-rotation/).

## One record per domain

A domain must not publish two SPF records (RFC 7208, section 3.2). When a new service tells you to "add this SPF
record", merge its `include:` into the record you already have, before the final `all`. Two records starting with
`v=spf1` give every receiver a `permerror`, even if each record is fine on its own.

Every name that sends mail needs its own record, subdomains included. A domain that sends no mail at all should
publish `v=spf1 -all`, which section 10.1.2 calls a well-established best practice. The
[SPF generator](https://email-validation.org/tools/spf-generator/) writes a single, merged record from the services you pick.

## What the all qualifiers mean

The last term says what happens to a server the record does not list. Its prefix, the qualifier, decides the result
(RFC 7208, sections 4.6.2 and 8):

| Ending | Result | What receivers do with it |
|---|---|---|
| `+all` (or plain `all`) | pass | Any server on the internet passes. Never publish it. |
| `?all` | neutral | Treated exactly like having no SPF record. |
| `~all` | softfail | Probably not authorized; receivers should not reject on this alone. |
| `-all` | fail | Not authorized; what happens next is the receiver's policy. |

A record without any `all` ends in neutral, as if `?all` were there (section 4.7). Terms after `all` are ignored, and
so is a `redirect=` in the same record (section 5.1). Microsoft recommends `-all` once DKIM and DMARC are in place,
and keeps `?all` for testing only. Gmail's
[SMTP error list](https://knowledge.workspace.google.com/admin/support/troubleshooting/gmail-smtp-errors-and-codes)
uses [550 5.7.26](https://email-validation.org/bounce-codes/550-5-7-26/) for mail that fails SPF under a `-all` record, and
[550 5.7.27](https://email-validation.org/bounce-codes/550-5-7-27/) for a bulk sender's SPF failure. Other servers report a failed check as
[550 5.7.23](https://email-validation.org/bounce-codes/550-5-7-23/) and an SPF error, such as an over-limit record, as 5.7.24
([RFC 7372, section 3.2](https://www.rfc-editor.org/info/rfc7372/)).

## Questions

### Can I split SPF into two TXT records to get more lookups?

No. Two SPF records are a `permerror` on their own, and the limit counts lookups across the whole evaluation, not per
record. What you can split is one long record into several quoted strings inside a single TXT record. To get a
fresh budget, use a subdomain.

### Should I end with `~all` or `-all`?

What happens next is up to the receiver. RFC 7208 says a softfail alone should not lead to a rejection, while a
fail may. `-all` is the clearer statement once you are sure every service is listed. If you are still finding
senders, `~all` with DMARC reports is a reasonable step. See
[moving DMARC from none to reject](https://email-validation.org/guides/dmarc-none-to-reject/).

## Try it

Run your domain through the [SPF checker](https://email-validation.org/tools/spf-checker/) to see the lookup total, and test an address you mail
with the free [email checker](https://email-validation.org/email-checker/).

## Related

- [SPF generator](https://email-validation.org/tools/spf-generator/) and [DMARC checker](https://email-validation.org/tools/dmarc-checker/).
- [Bounce code 550 5.7.23](https://email-validation.org/bounce-codes/550-5-7-23/): SPF check failed.
- [Glossary: SPF, DKIM and DMARC](https://email-validation.org/glossary/#spf-dkim-dmarc).

## Related guides

- [DKIM selectors and key rotation](https://email-validation.org/guides/dkim-selectors-key-rotation/)
- [Moving DMARC from p=none to p=reject](https://email-validation.org/guides/dmarc-none-to-reject/)
- [How to read DMARC aggregate reports](https://email-validation.org/guides/dmarc-aggregate-reports/)
- [Gmail and Yahoo sender requirements](https://email-validation.org/guides/gmail-yahoo-sender-requirements/)
- [Outlook.com requirements for high-volume senders](https://email-validation.org/guides/outlook-sender-requirements/)
- [All guides](https://email-validation.org/guides/)

## Sources

- [RFC Editor: RFC 7208, Sender Policy Framework (SPF)](https://www.rfc-editor.org/info/rfc7208/): lookup and void
  lookup limits (4.6.4), record selection (3.2, 4.5), record size (3.3, 3.4), qualifiers and results (4.6.2, 4.7,
  5.1, 8), `include` and `ptr` (5.2, 5.5), record design (10.1).
- [Microsoft Learn: Set up SPF to identify valid email sources](https://learn.microsoft.com/en-us/defender-office-365/email-authentication-spf-configure):
  counting nested includes, partial failures, subdomains, flattening advice, `-all` and `?all`.
- [RFC Editor: RFC 9989, DMARC](https://www.rfc-editor.org/info/rfc9989/): alignment and its relaxed default (4.4,
  4.7).
- [Google: Gmail SMTP errors and codes](https://knowledge.workspace.google.com/admin/support/troubleshooting/gmail-smtp-errors-and-codes):
  550 5.7.26 and 550 5.7.27.
- [RFC Editor: RFC 7372, Email Authentication Status Codes](https://www.rfc-editor.org/info/rfc7372/): the 5.7.24
  SPF error code (3.2).

## Get up to 500 free credits. No card needed.

New accounts get 500 free credits (100 when you sign up with a free email provider such as Gmail, Outlook or Yahoo, or another personal mailbox such as a paid one like Fastmail or one from your internet provider). Try it on your own list before you buy. [Create your account](https://email-validation.org/signin/?new=1) or [open the free email checker](https://email-validation.org/email-checker/).
