Ruvalin

SPF · diagnosis

SPF: the ten DNS lookup limit

RFC 7208 forbids the evaluation of an SPF record from triggering more than ten DNS queries. Past that, evaluation stops with a permerror and the record stops protecting the domain. The include, a, mx, ptr, exists and redirect mechanisms all count; ip4 and ip6 are free.

By Tom GernezUpdated 5 min read

Key points

  • The limit is ten lookups, set out in §4.6.4 of RFC 7208.
  • An include counts as one, plus whatever the included record consumes in turn.
  • ip4 and ip6 cost nothing: they trigger no query at all.
  • Going over is silent. No message bounces, and the domain believes itself protected.
  • A second, less-known limit caps empty answers (void lookups) at two.

What the standard says

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.
RFC 7208, §4.6.4

The reason for the limit is defensive: without it, a maliciously constructed SPF record could make any server evaluating it perform hundreds of DNS queries, turning it into an amplifier for a denial-of-service attack.

The consequence for you is blunt: past ten, evaluation does not return “fail”, it returns permerror. And a permerror is not a fail: most receiving servers then treat the message as though no SPF record existed at all.

Counting the lookups

MechanismCostNote
ip4:0An address or block, written out. Nothing to resolve.
ip6:0Same.
a1Resolves the domain's A record.
mx1 (+1 per host)Resolves the MX records, then each host's address. Expensive.
include:1 + the included record's costThe main trap: the count is recursive.
exists:1Rare outside macros.
ptr1 (+1 per name)Discouraged by the standard itself. Remove it.
redirect=1 + the target's costCounts like an include.
all0The final qualifier. No query.

Recursion is what makes records overflow: an include costs one lookup for itself, plus whatever the included record consumes in turn. A provider that nests three inside it consumes four.

A record close to the limit
v=spf1 include:_spf.google.com include:sendgrid.net
       include:mailgun.org include:servers.mcsv.net
       include:spf.protection.outlook.com mx a -all

Five services at one lookup each, plus a and mx, which costs one more per listed host: that reaches nine or ten. The record is syntactically perfect and it still holds. The next tool added tips it over, and nobody will see it happen.

The second limit: empty answers

The same RFC caps void lookups at two: queries that come back empty, with NXDOMAIN or no answer. Past two, that is also a permerror.

This is how ageing records fail: an include pointing at a provider you parted with, whose SPF domain no longer exists, consumes a void lookup. Three former providers are enough to break an otherwise correct record.

Getting back under the limit

  1. Delete what is no longer used. The first pass is nearly always enough: former providers, the tool trialled for six months, the ptr inherited from a configuration template. Free, and risk-free.
  2. Remove mx and a if they are pointless. They are in almost every record out of habit. Your receiving server is not necessarily your sending server; if it is not, mx does nothing but consume lookups.
  3. Delegate by subdomain. Sending each family of services from a dedicated subdomain, mail.example.com for the newsletter, invoices.example.com for billing, gives each its own budget of ten lookups. It is the clean answer, and it makes the reports easier to read too.
  4. As a last resort, replace an include with its ip4 blocks. It works, and it creates a debt: see below.
Check what is published
dig +short TXT example.com | grep spf1

Flattening, and why it is a trap

Flattening means replacing an include with the list of IP addresses it resolves to at the moment you look. The lookup count drops, the record becomes valid again, and the problem appears solved.

It is not: it has been moved in time. Those addresses belong to your provider, who changes them whenever they like without telling you, which is precisely why they publish an include rather than a list. The day they add a block, your messages fail SPF with nothing having changed on your side.

What SPF does not do, even under the limit

SPF checks the Envelope-From, an address your recipient never sees. A perfect record therefore stops nobody displaying your domain in the field a human reads: only DMARC, by requiring the two to align, closes that door.

SPF also does not survive forwarding, since the IP address changes in transit. That is why DKIM is not optional: in a world where messages get forwarded, it is the only one of the two checks that holds.

Common questions

Sources

Definitions

  • DMARC: the complete guide

    What DMARC is, how to read the record, how to publish it and how to move it to rejection without breaking your mail. Written by a consultant who deploys it, with the exact values.

  • Moving DMARC to reject without breaking your mail

    The rollout to p=reject, stage by stage, over two to three weeks. How to read the reports, align every legitimate sender, and how to tell when it is safe to tighten.

  • What DMARC does not stop

    DMARC blocks exact spoofing of your domain and nothing else. Cousin domains, misleading display names and compromised mailboxes all go straight through. What that means in practice.

Want to know what is published on your domain? The check is free, takes no sign-up, and reads only public records. Check your domain

Tom Gernez · Independent information security consultant. Ruvalin sets up email authentication for small French firms and professional practices.

All guides

CallBook