SPF Record Syntax
Sender Policy Framework (SPF) provides a method for domain owners to authorize use of their domain in the “MAIL FROM” and “EHLO” portions of an email transaction by specific servers and networks. This authorization is done through publishing a DNS TXT record, and verification of this authorization by receiving domains can be an effective step in fighting fraudulent email. SPF does not directly address forgery of the visible From address in an email message, but along with DKIM it is one of the underpinnings of DMARC, a protocol that is designed to stop that kind of forgery.
In this article, we explain the syntax of an SPF record and provide examples of common configurations. We also show how to set up an SPF record using the AWS Route 53 web service as an example provider. However, this article is not otherwise AWS-specific, and the ideas presented should be useful in deploying SPF on any provider. Finally, we’ll wrap up with best practices for implementing SPF without breaking anything and discuss how to avoid common issues.
Before we begin, here’s a brief summary of the different elements of an SPF record to serve as a reference throughout the rest of the article. Some of these might seem intimidating right now, but fret not: the concepts will be explained more thoroughly as we work through the syntax of SPF.
|Mechanisms||Determine when a directive should go into effect|
|all||This means an SPF directive should always be applied|
|ip4||The directive goes into effect, but only when the IP of sender matches a specific IPv4 address or subnet.|
|ip6||Same as above, but for IPv6 addresses.|
|a||Only apply the directive if the A or AAAA record of the queried or specified domain contains the sender’s IP.|
|mx||Only apply the directive if the MX record of the queried or specified domain contains the sender’s IP.|
|ptr||Only apply the directive if the MX record of the queried or specified domain contains a PTR record containing the sender’s IP. (Note: RFC 7208 recommends against using the ptr mechanism)|
|exists||Applies the directive if any A record is found on the provided domain.|
|include||Look up the SPF record of the specified domain. If that policy passes, then so does this one. Otherwise, processing continues.|
|Modifiers||Optional extensions to the original SPF framework|
|redirect||Defer entirely to the policy of another domain|
|exp||A domain featuring an explanation that will be provided to the sender for why a message failed.|
|Qualifiers||Determine the behavior when a mechanism is matched|
|+||PASS. This qualifier is optional, thus +mx and mx are the same.|
|–||FAIL. The mail should be rejected.|
|~||SOFTFAIL. The mail is not rejected, but “tagged”; often results in mail appearing in a spam folder.|
|?||NEUTRAL. Neither PASS nor FAIL. Equivalent to having no SPF policy.|
Anatomy of an SPF record
An SPF record starts with a version identifier (always v=spf1) and then includes a variable number of mechanisms. Each mechanism has a qualifier: If there is no explicit qualifier, then + (PASS) is assumed. Optionally, modifiers may appear, but each modifier can only appear once. For example, let’s say we want to authorize Google Workspace to send mail on our behalf and SOFTFAIL anyone else. We would use the “include” modifier to include Google’s SPF content and reject others:
v=spf1 include:_spf.google.com ~all
Or maybe you don’t even have mail setup for your domain, and thus wish to fail anyone attempting to send from your domain:
Mail that is rejected with FAIL (i.e., -all) can be returned to sender with a 550 SMTP error. The exact phrasing of the error message will vary by Mail Exchange provider, but they usually look fairly similar—Figure 1 shows an example rejection of an email sent via an unauthorized Gmail account being rejected by Microsoft Exchange for violating the sending domain’s SPF policy:
However, an important caveat is that DMARC can pass a message that fails SPF if it passes DKIM. Because DMARC only requires a pass with either SPF or DKIM, current email best practice is to not reject messages solely for an SPF fail unless the SPF record is v=spf1 -all. This article is focused on SPF, but in production it’s of paramount importance that administrators pay attention to DMARC (and consequently, DKIM) when evaluating what will happen to a message after passing or failing SPF.
Armed with an elementary idea of how an SPF record is laid out, let’s cement that understanding by looking at example SPF records that solve common problems.
Common uses of SPF
SOFTFAIL mail from IPs outside allowed subnet
Let’s say your mail servers are all in the same subnet, but you are not too confident and want mail sent from exchanges outside that subject to still arrive. You could use a SOFTFAIL so that the mail will be marked as suspicious in the inbox of the recipient but will still show up (however, be aware that if DMARC is in place and an incoming email passes DKIM, it’s possible that a SOFTFAIL from SPF can still result in delivery to the inbox without being marked as suspicious).
Such a setup is as simple as the following:
v=spf1 ip4:192.168.0.0/16 ~all
We can include as many IP addresses, both IPv4 and IPv6, or even subnet ranges as we wish. For example, we could amend the previous record like so:
v=spf1 ipv4:188.8.131.52 ipv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334 ip4:192.168.0.0/16 ~all
Redirect to a policy hosted on another domain
This is a common setup for organizations with many domains that prefer to have a single source of truth for SPF hosted on a single domain instead of scattering varied SPF policies across many domains.
FAIL if sender IP doesn’t match MX record
v=spf1 mx -all
Imagine our domain has some MX records that point to our mailservers. We expect that legitimate mail from our domain should only ever be sent from the IPs of those servers. This policy means that in order to pass SPF, one of the domain’s MX records should have an A record containing the IP of the sender. In other words, we will fail the sender if their IP doesn’t match a mailserver in the domain’s MX records.
Reject all mail
v=spf1 -all exp=yourdomain.com
This policy does two things. First, it rejects any mail sent from the queried domain. Second, including the optional “exp” modifier will perform a TXT lookup on the queried domain and send it along with the rejection; that way, the sender can see why the message was rejected. Be aware, however, that this modifier is not commonly used, and you are unlikely to encounter it in any production environments.
A policy like this also covers domains that don’t have email set up, in which case it’s safe to assume that any mail received from that domain is fraudulent.
Setting up SPF with AWS Route 53
Learning how to set up SPF on a live cloud deployment can make the concepts feel more solid. Below we’ll provide a visual guide to setting up SPF on a specific provider, but be warned that the specific UI details are liable to change in time. If you find that the AWS console no longer resembles the instructions below, we encourage you to consult with providers’ documentation directly. Here are some links to do just that, for various popular providers:
- Creating records by using the Amazon Route 53 console
- Add DNS records to connect your domain (Office 365)
- Google Cloud DNS | Manage Records
For our example, let’s use AWS’s Route 53 DNS provider to implement a live SPF record and verify that the record appears using the Unix networking tool dig. Although we’ll use AWS, the syntax of SPF is the same regardless of which DNS provider you choose.
First, access the Route 53 panel via the AWS web console and either create a record set or create a record within a set you’ve already created.
Create a TXT record containing the SPF record you want to implement. Your provider might offer a record defined as type SPF. Don’t use this—modern SPF records are hosted within TXT records. (See the following section on Best Practices and Common Mistakes for additional information on why this is.)
As an example TXT record just for testing, let’s demonstrate a record that will fail senders whose IPs don’t show up in the queried domain’s A, PTR, or MX records.
Apply the changes and wait about 60 seconds for the records to propagate. There’s no fixed amount of time you should wait, but it shouldn’t take more than a few minutes. After that, we can verify that the record is live by querying DNS. Let’s do exactly that using the nslookup command line tool:
$ nslookup -type=TXT domain.com Server: 127.0.0.53 Address: 127.0.0.53#53 Non-authoritative answer: domain.com text = "v=spf1 ip4:184.108.40.206/24 ip4:220.127.116.11/24 ip4:18.104.22.168/26 ip4:22.214.171.124/19 include:_spf.google.com include:_spf.qualtrics.com -all"
This result verifies that the record has been propagated sufficiently throughout the global network of DNS nameservers and should be considered in effect. That is, mail violating this policy may be rejected.
Too many lookups
This error is the most common headache that frustrates sysadmins trying to implement SPF. Many SPF mechanisms, as well as the redirect modifier, trigger additional DNS requests to determine whether a given sender is authorized. Imagine if a domain referred to another domain, which referred to another, and so on. This could lead to a denial of service attack in which mail exchanges were stuck performing many DNS lookups to authorize each email’s SPF record. To mitigate against such a scenario, the defining standards document for SPF explicitly limits the number of DNS lookups that a single record can trigger to just 10.
If your DNS records refer to other domains, be sure that those domains don’t, in turn, refer to too many others. If over 10 DNS lookups are triggered, you will receive an error saying “SPF PermError: Too Many DNS Lookups” or equivalent, the exact text varying by mail provider (source: RFC 7208 Section 4.6.4)
No sources of email
Imagine you create an SPF record and set the qualifier to hard fail, yet neglect to include any valid sources for email. No IP addresses, MX, A, or any other mechanisms for an email to actually pass SPF for your domain. This would cause all mail to hard fail SPF.
Therefore, when creating your domain’s SPF record, it’s of paramount importance that you review mechanisms that would allow an email to actually pass for your domain. Consult the table near the beginning of this article for examples of mechanisms that can be used with SPF to verify that a source is permitted to send for your domain.
SPF records are a good start for sysadmins hoping to reduce forgery, but if not implemented carefully, they can actually cause outages. When in doubt, try Valimail’s free tool to validate and interpret your SPF record before deploying. After you’ve verified that the record’s syntax is correct, you can also try the record on a test domain or subdomain and test everything out before deploying to any production domains.
Additionally, while it’s a good start, SPF is no longer considered sufficient by itself to prevent forgery. After SPF is up and working, consider looking into more modern frameworks like DMARC and DKIM as well.