Sunday, August 24, 2014

SPF Records and Too Many DNS Lookups

Some years ago, I added an SPF record to my domain. This makes it more likely that e-mails I send will get through to their recipients. The basic idea is that you declare (in your DNS record) which servers should be sending mail from your domain; any message not sent from one of those servers is probably spoofed. My SPF record for looked something like this:

v=spf1 ip4: mx -all

The IP address was for my server. The SPF record also includes the SPF record for DreamHost, since I often send mail from their shared mail servers; Fog Creek, since I use FogBugz to send e-mails to customers; and Amazon SES, which I use to send order confirmation e-mails and serial number lookups. The mx means to look up the mail servers in my domain’s MX DNS record. The -all means that these are the only authorized servers.

It turns out that many mail servers don’t care if your SPF record is invalid. But some do, and I found this out when I was unable to send messages to a customer who had configured his server more strictly. When moving my site to a different server, I had updated the SPF record with the new IP address but accidentally included a space before it, which made the SPF record syntactically invalid.

I fixed this and, after waiting for the DNS to propagate, validated my SPF record. As expected, the syntax was now correct. However, the SPF record was still invalid:

Results - PermError SPF Permanent Error: Too many DNS lookups

I had not seen that before, but it turns out that RFC 7208 says:

Some mechanisms and modifiers (collectively, “terms”) cause DNS queries at the time of evaluation, and some do not. 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, to avoid unreasonable load on the DNS. If this limit is exceeded, the implementation MUST return “permerror”.

My SPF record looked like it only had four lookups:

$ host -t txt | grep spf1 descriptive text "v=spf1 ip4: ip4: ip4: ip4: ip4: ip4: ip4: ip4: ip4: ip4: ip4: ip4:" " ~ALL"
$ host -t txt | grep spf1 descriptive text "v=spf1 ip4: ip4: -all"
$ host -t txt | grep spf1 descriptive text "v=spf1 ip4: ip4: ip4: -all"
$ host -t mx mail is handled by 0 mail is handled by 0

Unfortunately, the lookups within the and SPF records count, too. And those SPF records themselves have includes. The limit is quickly exceeded.

I was able to reduce the number of DNS lookups by removing, because combination of my server’s IP address and the MX record were sufficient. DreamHost itself sends e-mails via Google and SendGrid, but it doesn’t do this on my behalf. Likewise, I could replace with the IP addresses of the actual FogBugz mail servers, which are the only ones that FogCreek uses to send messages from my domain.

Now my SPF record looks like:

$ host -t txt | grep spf1 descriptive text "v=spf1 ip4: ip4: ip4: mx -all"

and it validates:

SPF record passed validation test with pySPF (Python SPF library)!

I will have to update it if any of the hard-coded IP addresses change, though.

2 Comments RSS · Twitter

Manually "flattening" SPF records might work at first, but if the hard-coded IP addresses change (which is reasonable enough), some automated SPF record flattening services go a long way.

For example, Dmarcly ( has a feature called Safe SPF, which automates SPF flattening for you. It keeps checking the underlying IP addresses, and if there is any IP change, Dmarcly picks it up and updates the flattened record. This way, there is no more outdated IP address, and of course, one will never run into the too-many-dns-lookups issue. In addition, you don't have to do anything manually!

Leave a Comment