# SPF too many DNS lookups: how to fix it

> Why SPF stops at 10 DNS lookups, which parts of the record count, how many lookups common email services cost (measured), and the fixes in order of safety, from deleting dead includes to moving a sender to a subdomain.

By MailSetupCheck. Updated 7 October 2026. Canonical URL: https://mailsetupcheck.com/spf-too-many-dns-lookups/

## The rule

SPF lists the servers allowed to send mail as your domain. To keep a single SPF check from turning into dozens of DNS queries, the standard caps the work. [RFC 7208, section 4.6.4](https://www.rfc-editor.org/rfc/rfc7208#section-4.6.4):

> The following terms cause DNS queries: the "include", "a", "mx", "ptr", and "exists" mechanisms, and the "redirect" modifier. SPF implementations MUST limit the total number of those terms to 10 during SPF evaluation [...] If this limit is exceeded, the implementation MUST return "permerror".

Three things trip people up:

1. **Nested lookups count.** An `include:` costs one lookup, plus every lookup inside the record it points to, all the way down.
2. **`ip4:`, `ip6:` and `all` are free.** So is `exp=`. Only the six terms above count.
3. **Lookups that find nothing have their own limit.** The same section says receivers "SHOULD limit 'void lookups' to two", meaning lookups that return no record at all. An include for a service you cancelled, whose record is gone, counts against both limits.

## What happens when you go over

A receiver checks the terms left to right and stops at the first one that matches the sending server. Mail from a service listed early may still pass. Mail from any service whose turn comes after the 10th lookup gets a permanent error, which counts as an SPF failure. Microsoft's own FAQ puts it plainly: "If you exceed 10 DNS lookups, your SPF check might fail."

A failed SPF check is not always fatal: DMARC passes if DKIM passes and aligns. But Gmail and Yahoo require bulk senders to have both SPF and DKIM working, and Microsoft requires both to pass for senders of more than 5,000 messages a day to Outlook.com. A broken SPF record also stops protecting you, since nothing after the limit is ever checked.

## Count yours

The checker resolves every include the way a receiver does, numbers each lookup, and names the one that breaks the limit. Open "SPF record" under "Records found" to see the tree.

> **Interactive checker.** On the HTML version of this page (https://mailsetupcheck.com/spf-too-many-dns-lookups/), enter a domain. Your browser asks Cloudflare's public DNS-over-HTTPS resolver (Google Public DNS as a fallback) for the domain's MX, SPF, DMARC, DKIM, MTA-STS, TLS-RPT and BIMI records, and the checker returns a fix list in three tiers: must fix to send to Gmail at volume, should fix, and nice to have, with the exact record to add where it can be worked out. MailSetupCheck has no server; the domain is never sent to it.

What it checks, in short:

- **MX**: present, null MX (RFC 7505), and the mail provider recognized from the host names.
- **SPF** (RFC 7208): exactly one v=spf1 record, valid syntax, every include and redirect followed, DNS-querying terms counted against the limit of 10 and empty lookups against 2, "+all", "?all", a missing "all", and "ptr".
- **DMARC** (RFC 9989, which replaced RFC 7489 in 2026): found at _dmarc.<domain> or by the DNS tree walk, every tag parsed, p=none explained, and an authorization record checked for report addresses at another organization (RFC 9990).
- **DKIM** (RFC 6376): your selector plus 34 common ones; key type and size (RFC 8301: at least 1024 bits, 2048 recommended). Not finding a key at these names does not prove DKIM is off.
- **MTA-STS** (RFC 8461), **TLS-RPT** (RFC 8460) and **BIMI** records.

DKIM selectors tried: `google` (Google Workspace (default)), `selector1` (Microsoft 365), `selector2` (Microsoft 365), `s1` (SendGrid), `s2` (SendGrid), `m1` (SendGrid (without automated security)), `k1` (Mailchimp (older setups)), `k2` (Mailchimp), `k3` (Mailchimp), `mte1` (Mailchimp Transactional (Mandrill)), `mte2` (Mailchimp Transactional (Mandrill)), `brevo1` (Brevo), `brevo2` (Brevo), `mail` (Brevo (older setups) and others), `km1` (Klaviyo (marketing)), `km2` (Klaviyo (marketing)), `kt1` (Klaviyo (transactional)), `kt2` (Klaviyo (transactional)), `ctct1` (Constant Contact), `ctct2` (Constant Contact), `mailjet` (Mailjet), `resend` (Resend), `zoho` (Zoho Mail (the example name in Zoho's guide; admins choose their own)), `fm1` (Fastmail), `fm2` (Fastmail), `fm3` (Fastmail), `protonmail` (Proton Mail), `protonmail2` (Proton Mail), `protonmail3` (Proton Mail), `sig1` (iCloud+ custom email domains), `x` (MXroute), `default` (cPanel and other hosting control panels), `cf2024-1` (Cloudflare Email Routing (signs forwarded mail)), `dkim` (Generic name some providers and admins use).

## What common services cost

Each row is what adding that service's include to your record costs: 1 for the include itself, plus its own lookups. Measured with this site's checker from live DNS on 8 October 2026. Vendors change these records without notice, which is why Google's and Microsoft's now cost a single lookup each.

| Service | Include | Lookups it costs you |
|---|---|---|
| Freshdesk | `include:email.freshdesk.com` | 7 |
| iCloud+ custom domains | `include:icloud.com` | 5 |
| Mailgun | `include:mailgun.org` | 5 |
| Zoho Mail | `include:zohomail.com` | 2 |
| Proton Mail | `include:_spf.protonmail.ch` | 2 |
| SendGrid | `include:sendgrid.net` | 2 |
| Salesforce | `include:_spf.salesforce.com` | 2 |
| Google Workspace | `include:_spf.google.com` | 1 |
| Microsoft 365 | `include:spf.protection.outlook.com` | 1 |
| Fastmail | `include:spf.messagingengine.com` | 1 |
| Amazon SES (custom MAIL FROM) | `include:amazonses.com` | 1 |
| Mailjet | `include:spf.mailjet.com` | 1 |
| Postmark | `include:spf.mtasv.net` | 1 |
| Mailchimp (older setups) | `include:servers.mcsv.net` | 1 |
| Mailchimp Transactional (Mandrill) | `include:spf.mandrillapp.com` | 1 |
| Zendesk | `include:mail.zendesk.com` | 1 |
| Help Scout | `include:helpscoutemail.com` | 1 |
| Shopify | `include:shops.shopify.com` | 1 |
| Campaign Monitor | `include:_spf.createsend.com` | 1 |
| Marketo | `include:mktomail.com` | 1 |


Raw data: [spf-include-costs.json](/tools/email-auth-check/spf-include-costs.json). Method: Each include was resolved with MailSetupCheck's SPF expander (engine/tools/email-auth-check/core.js) over Cloudflare DNS-over-HTTPS, counting include, a, mx, ptr, exists and redirect terms the way RFC 7208 section 4.6.4 does. cost_in_your_record = 1 (the include itself) + nested_lookups. Vendors change these records without notice; re-run data/mail/measure_spf_includes.mjs.

A few rows deserve a second look. `include:shops.shopify.com` resolves to `v=spf1 ~all`, which authorizes no servers at all. Salesforce's include uses an `exists:` term with a macro, which is resolved per message. Freshdesk's include alone costs 7.

## How to fix it, safest first

### 1. Delete includes for services you no longer use

Old newsletter tools, a help desk you left, a CRM trial. Each one costs lookups, and if the vendor has removed its record, it also counts as a void lookup. Ask whoever manages your domain what every include is for. If nobody knows, look for the vendor in your billing records before you delete it.

### 2. Delete includes that do nothing for you

SPF checks the domain in the message's bounce address (the `Return-Path`), not the From address you see. Many sending services use their own bounce domain, so receivers check SPF against the service's domain, not yours. For mail like that, the service's include in your record costs lookups and adds nothing. DMARC then passes through DKIM, as long as the service signs with your domain.

To check: send a message through the service to a Gmail address, choose "Show original", and read the `Return-Path`. If it is not your domain or a subdomain of it, the include is not doing anything for that service's mail. Make sure the same message shows `dkim=pass` with your domain and `dmarc=pass` before you remove the include.

### 3. Replace `a` and `mx` with the addresses they stand for

`a` and `mx` each cost a lookup, and `mx` also has to look up every mail server's address. If they refer to servers you run with fixed addresses, list those addresses with `ip4:` and `ip6:`, which are free. Do not do this for a hosted provider's servers; their addresses change.

### 4. Move a sending service to a subdomain

Send newsletters as `news.example.com` and invoices as `billing.example.com`. Each subdomain gets its own SPF record with its own 10-lookup budget, and its own DKIM. With DMARC's default relaxed alignment, mail from `news.example.com` still aligns with your organization's domain. This is the cleanest fix when you genuinely need many services.

### 5. Flatten only with automation

"Flattening" replaces includes with the IP ranges they currently contain. It works until a vendor adds a server, and then that vendor's mail quietly starts failing. Microsoft asks customers not to flatten its include. If you flatten, use a service or script that re-reads the vendor records every day and updates yours.

## Other SPF mistakes the checker catches

- **Two SPF records.** A domain may have only one `v=spf1` record; with two, every receiver returns a permanent error. Merge them into one (the checker writes the merged record).
- **`+all` or a bare `all`.** Authorizes every server on the internet. End with `~all` or `-all`.
- **`?all` or no `all`.** Mail from unlisted servers gets a neutral result, so SPF tells receivers nothing.
- **`ptr`.** RFC 7208 says it "SHOULD NOT be published": slow, unreliable, and ignored by some receivers.
- **Terms after `all`, or `redirect=` next to `all`.** Receivers never read them.
- **Includes that point to nothing.** A permanent error, as if the record were broken.

Sources: [RFC 7208](https://www.rfc-editor.org/rfc/rfc7208), [Google's SPF setup guide](https://knowledge.workspace.google.com/admin/security/set-up-spf), [Microsoft's SPF guide](https://learn.microsoft.com/en-us/defender-office-365/email-authentication-spf-configure).
