SPF PermError: How to Fix the 10 DNS Lookup Limit

An SPF record can exceed the DNS lookup limit even with only a few includes. Trace nested records, find the real cause of PermError, and fix it safely.

An SPF record can look perfectly reasonable until a receiver tries to evaluate it. You may have only three include: entries in your DNS, yet the receiving server reports spf=permerror because those entries refer to other SPF records, which refer to more records.

The problem is usually not the length of the TXT value. It is the amount of DNS-dependent work the receiving server must perform for a particular sending IP. RFC 7208 limits that work, and exceeding the limit produces a permanent SPF evaluation error.

Here is how to find the expensive part of a record and fix it without accidentally authorizing the wrong servers.

What SPF PermError means

You might see something like this in a receiving server's Authentication-Results header:

Authentication-Results: mx.receiver.example;
    spf=permerror smtp.mailfrom=example.com;
    dkim=pass header.d=example.com;
    dmarc=pass header.from=example.com

This is a possible outcome, not a contradiction. SPF failed to evaluate, but a valid, aligned DKIM signature still allowed DMARC to pass.

permerror is not the same as fail. An SPF fail is a result of evaluating a policy that says the sending IP is not authorized. A permerror means the SPF policy could not be evaluated correctly because of a permanent problem, such as excessive DNS lookups, malformed syntax or multiple SPF records at one name.

A receiver decides how to handle that message according to its own policies and other authentication signals. Do not assume every SPF permerror will cause a rejection, or that a message accepted by one mailbox provider is correctly configured.

The 10-lookup limit is not ten DNS packets

RFC 7208 Section 4.6.4 sets a maximum of 10 evaluated DNS-querying SPF terms per SPF check. These terms count toward the limit:

SPF term Counts toward 10? What to watch for
include:domain Yes Evaluating the referenced SPF record can consume additional terms.
a or a:domain Yes Authorizes addresses from a hostname's A/AAAA records.
mx or mx:domain Yes Also subject to a separate address-lookup limit for MX hosts.
exists:domain Yes Often appears in SPF policies using macros.
ptr Yes Discouraged by RFC 7208; avoid publishing it.
redirect=domain Yes, if evaluated Transfers evaluation to another SPF record.
ip4: and ip6: No Literal IP ranges require no DNS-querying SPF term.
all No Ends evaluation when reached.
exp= No Its optional explanation lookup is not part of this term limit.

The initial TXT lookup for the domain being checked is not counted as one of the ten terms. A lookup triggered inside a nested include: does count against the same overall budget. The budget does not reset each time an included record is opened.

The standard counts terms evaluated along a particular execution path. It does not simply add every include: written anywhere in the policy. SPF processes mechanisms from left to right, and an early match can stop evaluation before later terms are reached. That is why one sending service may show spf=pass while another service using the same domain hits permerror.

A record with two includes can require more than two lookups

Consider this simplified example. All domains and IP addresses below are illustrative.

example.com:
  v=spf1 include:_spf.outbound.example.net include:_spf.marketing.example.net -all

_spf.outbound.example.net:
  v=spf1 include:_spf.region1.example.net include:_spf.region2.example.net -all

_spf.region1.example.net:
  v=spf1 ip4:192.0.2.0/24 -all

_spf.region2.example.net:
  v=spf1 a:relay.example.net -all

_spf.marketing.example.net:
  v=spf1 ip4:203.0.113.0/24 -all

Suppose a message comes from 203.0.113.42, and relay.example.net does not resolve to that address. To reach the matching range in the marketing record, the receiver evaluates:

  1. include:_spf.outbound.example.net — term 1.
  2. Its include:_spf.region1.example.net — term 2; the IP does not match.
  3. Its include:_spf.region2.example.net — term 3.
  4. The nested a:relay.example.net — term 4; no match.
  5. include:_spf.marketing.example.net — term 5; its ip4: entry matches without using another term.

There are two includes in the main record but five counted terms along this path. If the provider behind either include adds more nested mechanisms later, the same mail flow can cross the limit without anyone editing example.com.

This also explains why checking only the number of visible includes is insufficient. You need to inspect the recursive evaluation for each real sending source.

Find which SPF domain the receiver actually checked

Before changing DNS, open the headers of a message sent through the affected service. Look for the receiving server's Authentication-Results and the smtp.mailfrom identity, or examine the SMTP envelope information if you manage the sending MTA.

From: Notifications <notifications@example.com>
Return-Path: <bounce-123@mailer.example.net>
Authentication-Results: mx.receiver.example;
    spf=permerror smtp.mailfrom=mailer.example.net

In this example, the SPF record relevant to the failed MAIL FROM check is at mailer.example.net, not automatically at example.com. The visible From: domain matters for DMARC alignment, but SPF itself checks the SMTP MAIL FROM identity (or the HELO identity in applicable cases).

If you edit the apex SPF record without confirming the actual SPF domain, you may change the wrong record and leave the error untouched.

A good first check is the published TXT data:

dig +short TXT mailer.example.net

Look for the value beginning with v=spf1. Other TXT records on the same hostname are not automatically a problem. Two separate TXT records both beginning with v=spf1 are a problem: RFC 7208 requires permerror when more than one SPF record is selected for the same domain.

For an included domain, query it too:

dig +short TXT _spf.outbound.example.net

Repeat for the referenced records. A plain dig trace tells you what DNS publishes; by itself, it does not execute SPF's left-to-right matching rules or compute an exact result for a specific IP. Use an SPF evaluator for that step.

You can start with the DeliverSight SPF checker to inspect the live policy and its DNS dependencies, then compare the result against a newly sent message.

Fix the cause, not just the number

Remove senders you no longer use

An abandoned email platform often leaves its include: behind for years. Check the actual inventory: transactional mail, newsletters, helpdesk, CRM, invoicing, monitoring, and any self-hosted relays.

Before deleting an entry, confirm which service owns it and whether it still sends for the SPF domain. Review mail logs and DMARC aggregate reports where available. A sender that is quiet this week might still handle password resets or annual invoices.

Remove retired services first. This reduces the number of dependencies without requiring you to maintain someone else's IP list.

Question a, mx, and ptr

Many SPF records contain a or mx simply because a template included them. But the hosts that receive your mail are not necessarily the servers that send it. If your MX points to a hosted mailbox service while your application sends through a separate outbound provider, the mx mechanism may add work without authorizing useful sending IPs.

Verify the sending infrastructure before removing these mechanisms. If a host really does send mail, retain a correctly scoped authorization for it. Avoid ptr; the SPF standard explicitly discourages publishing that mechanism.

Use literal IP mechanisms for addresses you control

For a stable, self-managed outbound server, a literal IP can be clearer than an unnecessary DNS lookup:

v=spf1 ip4:192.0.2.10 ip6:2001:db8::10 -all

The addresses here are documentation examples. Do not publish them unchanged.

This approach is less attractive when a third-party provider rotates IP ranges. Converting the provider's SPF record into a fixed IP list shifts maintenance responsibility to you. If the provider changes its infrastructure and your list becomes stale, legitimate mail can begin failing SPF without any obvious change in your DNS.

Separate genuine sending domains when appropriate

If different services can use different envelope sender domains, their SPF policies do not have to share one crowded record. For example, a transactional service may use bounce.example.com, while a marketing provider uses news.example.com.

This requires actual configuration in those services. Creating SPF records on subdomains alone does not make mail use them. Check the smtp.mailfrom value in a newly delivered message. Also check DMARC alignment with the visible From: domain; moving the envelope sender can change whether SPF helps DMARC pass.

Treat SPF flattening as a maintenance trade-off

SPF flattening resolves referenced provider records into IP ranges and replaces DNS-querying terms with ip4: or ip6: entries. It can lower the lookup count at that moment, but it is not a permanent cure.

Providers add and remove IP addresses. A flattened record can become outdated, authorize addresses that no longer belong to the service, or exceed practical DNS response-size limits. If you must flatten, use a trustworthy update process, monitor the generated record, and verify how quickly it reacts to provider changes. Do not copy a snapshot of a large provider's IPs into production and assume it will remain correct indefinitely.

Avoid the opposite shortcut, too: replacing -all with +all does not repair SPF. It simply authorizes every source that reaches that mechanism.

Two other ways to get PermError below ten terms

Void lookups. RFC 7208 recommends limiting DNS lookups that return NXDOMAIN or an empty answer to two. Receivers implementing that recommendation can return permerror on further void lookups even though the ten-term budget was not exhausted. Broken or obsolete referenced hostnames are worth investigating.

MX expansion. An mx mechanism has an additional processing constraint: it must not require more than ten address-record lookups for its MX hosts. This is separate from the overall count of DNS-querying SPF terms. An SPF record containing a single mx is not automatically safe.

Malformed syntax, invalid referenced policies and multiple SPF records can cause permerror as well. If your evaluator reports a different cause, do not assume the lookup limit is responsible just because your record contains several includes.

Verify the repair from the sending service

A corrected DNS record is only the first check. Retest each legitimate sender, including those that use custom bounce domains.

  1. Query the TXT record at the actual MAIL FROM domain and confirm that only one SPF record is selected.
  2. Re-evaluate the policy for a representative sending IP, including nested includes and other limits.
  3. Send a fresh message through the service. Check the receiving system's trusted Authentication-Results for spf=pass and the intended smtp.mailfrom domain.
  4. Check DMARC separately. SPF pass helps DMARC only when the SPF-authenticated domain aligns with the visible From: domain; alternatively, a passing, aligned DKIM signature can satisfy DMARC.
  5. Repeat for other active providers. An SPF result from one sender does not prove every sending path is healthy.

If SPF is passing but DMARC is failing, see why SPF and DKIM can pass while DMARC alignment fails. For the full meaning of each record mechanism, use the SPF mechanisms and modifiers reference.

The durable target is not a cosmetically short TXT record. It is a policy that authorizes only your real sending systems, stays within the evaluation limits for those systems, and can survive routine provider changes.


Technical references