Spam E-Mails are in todays day and age a part our daily business. Not only is it annoying, but also dangerous for various reasons. That’s why we need to protect ourselves such as everybody else as good as we can from being scammed (or worse). Using a simple technology called the sender policy framework (SPF), we can prevent other parties to send E-Mails in our names very effectively. This way you can stop crimminals to abuse your domains and E-Mail addresses for sending spam.
Note: This is an intermediate guide (Even if the title says otherwise). I recommend for you to have at least basic knowledge about DNS zones and records. Otherwise it may be hard to understand, eventhough I try to explain it as clear as possible.
What is the Sender Policy Framework
The sender policy framework is an E-Mail validation mechanism, for verifying that specific IP addresses and / or servers are permitted to send E-Mails using certain sender domains. The mechanism is defined within the DNS zone of your domains using a TXT record and validated by the recieving E-Mail servers. For example, if your mail service and website are hosted on a dedicated IP address, you can include this address in your SPF record, to allow your server to send E-Mails using your domain and deny it from any other server. In my case I want E-Mails containing the domain «ligabue.io» only to be sent from my website, so I can recieve submitted forms and other Mails sent from WordPress and my server. Any other service that send E-Mails in my name should be marked as spam or be rejected.
The SPF record itself does not specifically mark E-mails as spam. If any E-Mail is sent to another address, the recieving server will check if any SPF record is set for the sender address / domain and validate it using the sender IP address. If the IP address is not listed within the record the recieving server will mark the incoming E-Mails as spam or reject them.
Note: Mail servers are in theory not obligated to take account of any SPF record. So if your SPF record is not correct, it may occur that certain recieving servers will not validate the E-Mails and they go through, while other servers with stricter rules will decline them or mark them as spam.
SPF Syntax
As said before, SPF records are simple TXT Records which can be used within any DNS zone. To declare a TXT record as SPF entry, you need to add the prefix «v=spf1» at the very beginning of the record.
v=spf1
After initiating the SPF record, you can use qualificators and several mechanisms to allow or deny certain sources to send E-Mails from the given domain. Qualificators and mechanisms, such as their functionalities are described in the following chapters.
v=spf1 [qualificator][mechanism]
You can leave the qualificator part empty, which will cause the default «+» to be used.
v=spf1 [mechanism]
Some of the mechanisms can handle certain kinds of values. the values can be passed by using a «:» sign.
v=spf1 [qualificator][mechanism]:[value]
Mechanisms refering directly to IP addresses such as ip4, ip6, a and mx, also allow IP prefix-lengths which are indicated by using a slash «/».
v=spf1 [qualificator][mechanism]:[value]/[prefix-length]
Multiple mechanisms are used by separating them with a space. The syntax for each individual mechanism is the same. There are some limitations to how many entries can be used, which are described further below.
v=spf1 [qualificator][mechanism] [mechanism_no_qualificator] [mechanism_with_value]:[value]
Note: Mechanisms are evaluated in order.
Qualificators
Qualificators are used in front of each mechanism to define weither the specified mechanism should be allowed or denied to send E-Mails.
+ (Pass)
This is the default qualificator for each mechanism. You are allowed to not actually use any qualificator at all, in which case the mechanism will be handled as with a «+» qualificator. «+» means, that the given mechanism is fully allowed to send E-Mails.
Both two SPF records below will be handled equally.
v=spf1 a mx
v=spf1 +a +mx
– (Fail)
«-» qualificators are used for preventing certain mechanisms to send E-Mails from you domain. If a mailserver recieves an E-Mail from any IP address included in a mechanism which uses this qualificator, it will be marked as spam or more commonly fully rejected. This qualificator ist mostly used in connection with the «all» mechanism:
v=spf1 -all
This record won’t allow any E-Mails to be sent from your domain at all. (Read more about the «all» mechanism further below)
~ (Soft Fail)
Although mechanisms marked with «~» will be handled as fail, the services who handle them are encouraged to not treat them to harsh. So depending on your spam filter, E-Mails may pass the firewall, but will be sent to your junk mail folder. (Caution: This scenario is just an example)
? (Neutral)
The neutral qualificator doesn’t tell the recieving E-Mail server to handle the given mechanism in a certain way.
Mechanisms
Mechanisms are different ways to include certain IP addresses in your record and allow them to send E-Mails from your domain. Each mechanisms is used with a qualifier. If no qualifier is defined, the default «+» will be used. As mentioned before, you can use multiple mechanisms by separating them with a space. There are some limitations on how often certain records can be used, which are desribed further below
all
The «all» mechanism always matches with any given sender IP address.
Note: The all mechanism should always be at the end of the SPF record ans is highly recommended to be used with a fail qualificator «-» or «~». Since SPF mechanisms are evaluated in the same order as they appear within the record, a «-all» at the beginning would cause every E-Mail to be handled as fail.
v=spf1 -all
This record does not allow any E-Mails to be sent from your domain.
v=spf1 mx -all
By using the mx mechanism we allow all IP addresses listed within the mx-records of the domain to pass. The «-all» mechansim at the end, will cause a fail if the sender IP does not match any of those records.
v=spf1 +all
Eventhough this is a valid record, I highly recommend to not use it. This one allows every IP address to send E-Mails in your name. Since this is very unsave, a lot of spam filters will handle domains using a record like this with caution. Some wont’t even accept it as a valid SPF record.
ip4
Using the ip4 mechanism you can include IPv4 addresses or whole IPv4 networks, by adding a prefix length. This mechanism requires a value.
This example only allows E-Mails to be sent from the specific IP address 192.168.1.1.
v=spf1 ip4:192.168.1.1 -all
Whereas this one let’s the whole range from 192.168.0.1 to 192.168.255.255 pass.
v=spf1 ip4:192.168.0.1/16 -all
ip6
The ip6 mechanism works the same as the ip4 one, except that it is used for IPv6 addresses and networks.
The following example only allows E-Mails to be sent from the specific IP address 1080::8:800:200C:417A.
v=spf1 ip6:1080::8:800:200C:417A -all
By using a prefix length, the example below, lets any IP address in the range from 1080::8:800:0000:0000 to 1080::8:800:FFFF:FFFF pass.
v=spf1 ip6:1080::8:800:200C:417A/96 -all
a
The a-mechanism includes the IP address or range defined within the a-record of the given domain.
You can use the a-mechanism without any value. This way, the a-record of your domain will be used. An example for using this would be, if you have a website, containing a webform which should be allowed to send E-Mails.
v=spf1 a -all
Note: Some websites use SMTP services or other ways to send E-Mails, in which case this a-mechanim will not have any effect.
If you want to include the a-record from another domain, the domain can be passed as a value. This is useful if you want to include a service used by a subdomain. Let’s say we have an online shop at «shop.example.com». By using this method, we can inherit the IP address defined within the a-record for the «shop» subdomain.
v=spf1 a:shop.example.com -all
Since the a mechanisms points directly to an IP address, you are also allowed to use prefix-length to define a complete network to include. You can use this with the a record of the local zone, or an external one, as seen in the example above.
v=spf1 a/24 a:example.com/24 -all
mx
The mx-mechanism includes each mx-record defined for your domain or a specific domain you can pass as a value.
v=spf1 mx -all
As said you are allowed to access the mx-records of any other domain.
v=spf1 mx:example.com -all
You can also use the mx-mechanism to include complete IP ranges by just adding a prefix-length (subnet). This works for both of the examples above.
v=spf1 mx/24 mx:example.com/24 -all
Note: Since the mx-mechanism includes every mx-record of the defined domain (which is often more than one, for fallback reasons), it can easily blow up the amount of required DNS lookups. As described further below, SPF records have a limitation of 10 lookups.
include
Certain services provide their own SPF records, which can be included for your own domain. One of the most commonly used example for this is the one for Microsoft 365. Microsoft provides us with a SPF record, which contains all required IP addresses of their outgoing mailservers. We can access this record by using the include-mechanism.
v=spf1 include:spf.protection.outlook.com -all
Note: This mechanism only works if the target domain has a valid SPF record.
If you want to provide your own SPF record for your clients to use, you can do it easily by creating one for any specific subdomain. Let’s say, you provide mail services within the IP range 192.168.0.1/16. You can create a TXT record for your subdomain «spf.example.com» looking like this:
v=spf1 ip4:192.168.0.1/16 -all
Now your clients can use your subdomain with the include-mechanism in their own record.
v=spf1 include:spf.example.com -all
Common mistakes
Include-mechanism without valid SPF record on target domain
The behavior of the include-mechanism is often mistaken for the one the a-record provides. As explained, the include-mechanism is used to include other spf records and nest them into eachother. You can not use it to include the IP address of the domain included, unless the domain included has a valid SPF record containing the domain.
A little example:
v=spf1 include:example.com -all
This is actually a valid SPF record, but it will only works if the DNS zone for «example.com» actually has a valid spf record, which might look as followed.
v=spf1 a mx -all
If this is not the case, and «example.com» has no SPF record, your include mechanism will cause a permanet error for your SPF record, which can cause all of your mails to be rejected by certain servers.
If you want to access specific DNS records of a certain domain, which has no SPF record, you can do this by using the a- or mx-mechanism.
v=spf1 a:example.com -all
Too many DNS lookups (RFC 7208)
SPF records have are only allowed to make 10 DNS lookups at max (as defined in RFC 7208 – 4.6.4). Each mechanism (which requires a lookup), that exceeds this limitation will be ignored.
DNS lookups are made everytime a reference to a DNS record is made by any mechanism. This would include every single mechanism except for ip4 and ip6. For nested SPF records (using the include mechanism) every DNS lookup made by the child records is counted as well.
Note: Since the include-mechanism requires to make a DNS lookup itself, the mechanism counts too. So if you include a SPF record which uses 4 more DNS lookups, this single include-mechanism uses 5 of the 10 possible lookups.
The mechanims which will cause this limitation to be a problem most commonly are: exists, ptr, mx and include.
More mechanisms and modifiers
There are two more mechanisms (exists, ptr) and some modifiers (redirect, exp), which are not included within this guide. The reason for that is, that this is a beginner’s guide, which in my opinion should include the mechanisms and examples mostly used in practice. If you want me to explain the other ones too, please let me now in the comments.
Useful Links
http://www.open-spf.org/SPF_Record_Syntax/ (SPF record docs)
https://www.spf-record.com/spf-lookup (SPF validator and other tools)