SPF PermError: Too Many DNS Lookups and How to Fix It

Your SPF record looks fine, every sender you use is listed, and yet a checker reports "PermError: too many DNS lookups". That means receivers stop evaluating your record partway through and treat it as broken, so SPF passes for nobody. The cause is a hard limit of ten DNS lookups in the SPF specification, and the fix is almost always to remove things rather than add them.

What the limit actually is

SPF is defined in RFC 7208, and section 4.6.4 caps the number of terms that trigger a DNS query at ten per evaluation. If a receiver hits the eleventh, it must stop and return permerror.

The terms that count are:

  • include:
  • a
  • mx
  • ptr
  • exists:
  • the redirect= modifier

The terms that don't count are ip4:, ip6:, all and the exp= modifier, because they need no DNS query at evaluation time. The initial TXT lookup of your own domain doesn't count either.

The part that catches people is that the limit is global. The RFC says it must be tracked across every nested evaluation, not per record. So include:_spf.example-provider.com costs one lookup for itself plus every include, a or mx inside that provider's record, and inside whatever those include. Three providers can easily add up to eleven without you ever typing more than three includes.

There are two smaller limits in the same section worth knowing. Each mx mechanism may resolve at most ten address records, and there is a limit of two "void" lookups, meaning queries that return no answer or NXDOMAIN. Exceed either and you also get permerror.

Why a PermError hurts more than you'd think

A PermError isn't a soft warning. Under DMARC it doesn't count as an SPF pass, so your mail has to rely entirely on DKIM being present and aligned. If DKIM is also missing or misaligned for one of your sending services, that stream fails DMARC outright, and with p=quarantine or p=reject it goes to spam or bounces.

It also fails silently. Nothing tells you the day you add the service that tips you over. Some receivers are more forgiving than the RFC and carry on past ten, which is exactly why the problem can look intermittent: one mailbox provider delivers, another doesn't. You can't rely on leniency, so treat ten as a ceiling.

If you haven't read about how SPF, DKIM and DMARC fit together, our SPF, DKIM and DMARC explainer covers the basics. This post assumes you have an SPF record and it's broken.

Counting your lookups

The quickest way is our email security checker, which walks the whole include tree and reports the total. It's worth knowing how to do it by hand, though, because that's how you see where the lookups are going.

Start with your own record:

dig +short TXT example.com | grep spf1

Say it returns:

"v=spf1 include:_spf.google.com include:spf.protection.outlook.com include:mail.example-crm.com a mx ~all"

That's five lookups before you've looked inside anything: three includes, a and mx. Now query each include in turn:

dig +short TXT _spf.google.com

Every include, a, mx, exists or redirect you find in those answers adds one, and you repeat for any includes they contain. Write it down as a tree. You'll usually find one or two providers responsible for most of the count.

While you're in there, check you only have one record starting v=spf1. Two SPF records on the same name is its own PermError under section 4.5, and it's a common result of two people each "adding SPF" separately.

Ways to get back under ten

Work through these in order. The early ones are free and safe.

Remove senders you no longer use. Old newsletter tools, a helpdesk you cancelled, a CRM trial. Every stale include costs lookups, and an include pointing at a domain that no longer publishes SPF is worse, because RFC 7208 treats an include that returns no record as permerror in its own right.

Drop a and mx if they don't send mail. Many records carry a mx out of habit. If your web server and inbound mail servers never send outbound mail as your domain, those two mechanisms are wasted lookups. If they do send, replace them with ip4: and ip6: entries for the actual addresses, which cost nothing.

Remove ptr entirely. The RFC says it should not be published. It's slow, it's unreliable, and it costs a lookup.

Check whether the include is needed at all. This is the one most people miss. SPF checks the envelope sender (the Return-Path), not the From address. Many email platforms send with their own bounce domain, or a custom subdomain like bounce.example.com that you point at them. In that case SPF is evaluated against their domain or your subdomain, and the include in your root record does nothing. Look at the Return-Path header of a message the service sent. If it isn't your bare domain, you can likely remove the include, and DMARC can still pass through aligned DKIM or relaxed SPF alignment on the subdomain.

Move bulk senders to a subdomain. Sending marketing from news.example.com gives that subdomain its own SPF record with its own ten-lookup budget, and keeps a bad campaign's reputation away from your main domain.

Flatten, carefully. Flattening replaces includes with the IP ranges they currently resolve to. It works, but providers change their ranges without telling you, and a hand-flattened record quietly goes stale. Only flatten with a tool or service that re-resolves and republishes automatically, and never flatten the big cloud mail providers by hand.

Checking the fix

After editing, wait for the old TTL to expire, then re-run the check. You can confirm the new record is live from several resolvers with a DNS propagation check. Then send a test message to a mailbox you control and read the Authentication-Results header: you want spf=pass for each sending service, not just the main one.

Aim for eight or fewer. Sitting at exactly ten leaves no room, and the next time a provider adds a nested include on their side, you're back in PermError without having changed anything.

Frequently Asked Questions

Does ip4: count towards the ten-lookup limit?

No. ip4:, ip6: and all need no DNS query, so you can list as many addresses as you like, within the overall size of the record. That makes them the cheapest way to authorise servers you control.

Why does my SPF pass at one provider and fail at another?

Some receivers enforce the limit strictly and some are lenient, and DNS responses can differ between resolvers while a change propagates. If one says pass and another says PermError, assume the strict one is right and count your lookups.

Is the ten-lookup limit per include?

No, it's per evaluation. Every lookup triggered anywhere in the include tree counts against the same ten, including lookups inside records you don't control.

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

No. Two v=spf1 records on the same name is itself a PermError. You can split one long record into several quoted strings inside a single TXT record, which receivers join together, but that doesn't change the lookup count.

Was this useful?
Share

Related reading

0 comments

No comments yet. Be the first.

Leave a comment

Comments are reviewed before they appear. Your email is optional, is never published, and is only used if we need to reply.

« Back to Blog