Email Security

SPF Record Troubleshooting: Fixing Failures and the 10-Lookup Limit

Diagnose SPF failures with precision: understand softfail vs hardfail, fix the 10 DNS lookup limit, flatten complex SPF records, and validate your changes with the right tools.

August 19, 20259 min readShipSafer Team

SPF (Sender Policy Framework) is one of the oldest email authentication standards, and yet misconfigured SPF records are still one of the most common causes of email delivery failures and security gaps. The syntax is deceptively simple, but the interactions between multiple sending services, DNS lookup limits, and alignment requirements create a surprising number of failure modes.

This guide covers how to diagnose SPF failures systematically, what the 10-lookup limit actually means and how to fix it, and how to verify your changes before they affect production mail flow.

How SPF Works

When a receiving mail server gets a message, it extracts the envelope sender address (the MAIL FROM: address used in the SMTP transaction, also called the Return-Path). It then queries the DNS TXT record for that domain and evaluates whether the connecting IP address is authorized to send on behalf of the domain.

An SPF record looks like this:

example.com. TXT "v=spf1 ip4:203.0.113.0/24 include:_spf.google.com include:sendgrid.net ~all"

The mechanisms are evaluated left to right. The all mechanism at the end is the catch-all that determines what happens to emails that do not match any previous mechanism.

Understanding SPF Results

SPF evaluation produces one of several results:

  • pass — The IP is explicitly authorized. With +all or just all, everything passes (extremely bad practice).
  • fail — The IP is explicitly not authorized. The -all mechanism produces this result.
  • softfail — The IP is probably not authorized, but the domain owner is not confident. The ~all mechanism produces this.
  • neutral — The domain makes no assertion about the IP. The ?all mechanism.
  • none — No SPF record exists for the domain.
  • permerror — Permanent error, usually due to a malformed record or exceeding the 10-lookup limit.
  • temperror — Temporary DNS failure.

The important distinction is between ~all (softfail) and -all (hardfail).

Softfail vs Hardfail

With ~all (softfail), receiving servers typically accept the message but may mark it as spam. This is a safer default during initial SPF deployment because it reduces the risk of losing legitimate email.

With -all (hardfail), receiving servers can reject the message outright. However, the actual behavior depends on the receiving server's configuration — many servers treat hardfail the same as softfail in practice. The real value of -all combined with DMARC enforcement is that DMARC can then instruct servers to quarantine or reject failing messages consistently.

For maximum protection, you want -all in your SPF record and p=reject in your DMARC record. Neither alone is sufficient; they work together.

The 10 DNS Lookup Limit

RFC 7208 (the SPF specification) states that SPF evaluation must not result in more than 10 DNS lookups. Each of the following mechanisms triggers one DNS lookup:

  • include:
  • a:
  • mx:
  • ptr: (also strongly discouraged for performance reasons)
  • exists:
  • redirect=

IP addresses specified with ip4: and ip6: do not count as lookups since they are inline values.

The critical detail: lookups are counted recursively. When you include _spf.google.com, the SPF evaluator looks up that record, and then counts any additional include: mechanisms it finds within that record.

Here is a common scenario that hits the limit:

"v=spf1 include:_spf.google.com include:sendgrid.net include:mailchimp.com include:spf.protection.outlook.com include:amazonses.com include:spf.mandrillapp.com ~all"

That looks like 6 includes, but many of those ESP records themselves contain further includes. sendgrid.net's SPF record has multiple includes. spf.mandrillapp.com chains to additional records. You can easily exceed 10 total lookups with 5-6 includes at the top level.

When the 10-lookup limit is exceeded, the result is permerror, which many receiving servers treat as a hard failure.

Diagnosing SPF Failures

Step 1: Check the Authentication-Results Header

The most direct way to see why SPF is failing is to look at an actual email's headers. In Gmail, click the three-dot menu and choose "Show original." Look for the Authentication-Results header:

Authentication-Results: mx.google.com;
       spf=fail (google.com: domain of user@example.com does not designate 203.0.113.45 as permitted sender) smtp.mailfrom=user@example.com

The parenthetical explanation is what you need. Common reasons:

  • "does not designate [IP] as permitted sender" — the sending IP is not in the SPF record
  • "exceeds the maximum lookup count" — you have hit the 10-lookup limit
  • "domain of [address] has no SPF policy" — no SPF record exists

Step 2: Use Online Diagnostic Tools

MXToolbox SPF Record Checker (mxtoolbox.com/spf.aspx) — Parses your SPF record, shows all resolved includes, counts lookups, and identifies specific problems.

Google Admin Toolbox Check MX — Useful if you use Google Workspace and want to see what Google's servers specifically think about your record.

Mail-tester.com — Send a test email to a unique address they provide, and the tool shows a full authentication analysis including SPF, DKIM, and DMARC results.

Kitterman SPF Validator — Tests SPF authorization for a specific IP address against your record, simulating exactly what a receiving server would do.

Step 3: Count Your Lookups Manually

Use MXToolbox's "SPF Record Lookup" feature, which recursively expands all includes and shows the total lookup count. Alternatively, you can do this manually:

# Check your top-level SPF record
dig TXT example.com | grep spf

# Check what an include resolves to
dig TXT _spf.google.com

# Check nested includes
dig TXT _netblocks.google.com
dig TXT _netblocks2.google.com
dig TXT _netblocks3.google.com

Fixing the 10-Lookup Limit: SPF Flattening

SPF flattening is the process of replacing include: mechanisms with the actual IP ranges they resolve to. This eliminates the DNS lookup cost.

For example, instead of:

"v=spf1 include:sendgrid.net ~all"

You would use the actual IPs that sendgrid.net resolves to:

"v=spf1 ip4:167.89.0.0/17 ip4:169.45.0.0/16 ip4:198.37.144.0/20 ~all"

The problem with manual flattening is that ESPs change their IP ranges. When SendGrid adds or changes IP addresses, your hardcoded SPF record no longer covers them. Emails from their new IPs start failing SPF, and you may not notice until deliverability drops.

Automated SPF flattening services — such as AutoSPF, EasyDMARC's SPF optimizer, or dmarcian's SPF record editor — solve this by maintaining the flattened record and updating it automatically when underlying ranges change. They also handle the TXT record length limit (255 characters per string, multiple strings can be concatenated).

Alternative: Consolidate Sending Services

The better long-term solution is to reduce the number of services sending email from your primary domain:

  1. Route all transactional email through a single ESP (SendGrid, Postmark, or SES)
  2. Route all marketing email through a subdomain (marketing.example.com) with its own SPF record
  3. Use a smart host relay for internal application servers rather than sending directly
  4. Eliminate services that send email from your domain but that you no longer actively use

Using subdomains for different email streams not only solves the lookup limit but also improves DMARC reporting clarity and allows you to set different policies for different streams.

Multiple SPF Records

You can have only one SPF record per domain. If you publish multiple TXT records that look like SPF records (starting with v=spf1), the result is permerror. This is a surprisingly common mistake when adding a new sending service — sometimes the service's onboarding instructions say "add this SPF record" without mentioning you need to merge it with your existing one.

To check for duplicate SPF records:

dig TXT example.com | grep "v=spf1"

If you see more than one line with v=spf1, you need to merge them into a single record.

SPF Alignment and DMARC

SPF alone is not sufficient for DMARC compliance. DMARC requires that SPF passes and the From: header domain aligns with the envelope sender (Return-Path) domain.

When you send through a third-party ESP, the envelope sender is typically something like bounces.sendgrid.net or bounce.example-mailserver.com. Even if SPF passes for that domain, it does not align with example.com in the From: header.

This is why DKIM is more reliable for DMARC: a DKIM signature uses the d= parameter which must align with the From: domain. Most ESPs support configuring DKIM with your own domain, which provides DMARC alignment regardless of the envelope sender.

Check DMARC alignment in your aggregate reports — look for records where SPF passes but spf_aligned shows fail.

SPF Record Size Limits

A single DNS TXT record string is limited to 255 characters. However, DNS allows multiple strings within a single TXT record that are concatenated by the client. This means you can have very long SPF records by using multiple quoted strings:

example.com. TXT "v=spf1 ip4:203.0.113.0/24 ip4:198.51.100.0/24 " "ip4:192.0.2.0/24 include:_spf.google.com ~all"

The maximum total length of an SPF record after string concatenation is 512 bytes for UDP DNS responses, or up to 4096 bytes for TCP. In practice, if your SPF record is approaching 512 bytes, it is a sign that you have too many includes and should consolidate.

Validating Changes Before Deployment

Because SPF records take effect immediately (subject to DNS TTL caching), test changes in a staging environment first:

  1. Lower your SPF record's TTL to 300 seconds (5 minutes) 24 hours before making changes
  2. Use a subdomain-based test: publish the new SPF record at a test subdomain and validate it with diagnostic tools
  3. Check MXToolbox, Google Admin Toolbox, and Kitterman against the test subdomain
  4. Once validated, update the production record

After updating, monitor DMARC aggregate reports for the next 48-72 hours, specifically looking for any new SPF failures from known-good IP ranges that were working before.

Building a Sustainable SPF Record

A well-maintained SPF record follows these principles:

  • Use -all (hardfail) rather than ~all when you have confidence in your sender list
  • Keep the lookup count under 8 to leave headroom for future additions
  • Document every include: mechanism — which service it belongs to, when it was added, and who requested it
  • Review the record quarterly and remove includes for services you no longer use
  • Use subdomains for distinct email streams (marketing, transactional, alerts)
  • Combine SPF with DKIM for DMARC alignment, not SPF alone

SPF is a foundational control, but it is only as strong as its maintenance. An SPF record that was correct 18 months ago may have drifted as new services were added without updating the record or as legacy services were decommissioned but their includes left in place.

spf
email-security
dns
email-authentication
deliverability

Check Your Security Score — Free

See exactly how your domain scores on DMARC, TLS, HTTP headers, and 25+ other automated security checks in under 60 seconds.