Setting up SPF requires adding a TXT record to your domain DNS configuration.
For example, if your domain is dmarcwise.io
, you should create a record on that domain.
You may have multiple TXT records, but you cannot have multiple SPF records. Be careful, as this would generate a “permanent” SPF failure!
Here’s an example of an SPF record:
v=spf1 include:_spf.google.com ~all
What to include in the SPF record
The content of your SPF record depends on how you send emails:
- If you have your own servers, you should list all their IP addresses or IP prefixes (CIDR notation).
- If you use an email service provider, it’s likely you’ll be provided with an
include
directive to be included in your TXT record.
You should go through all the email service providers you use: not only the provider for your email inboxes, like Google Workspace, Microsoft 365 or your hosting provider of choice, but also email marketing and newsletter services.
Remember that SPF is checked on the Envelope From domain. It’s possible (and even frequent) that your email service provider may use a subdomain of your domain as the Envelope From domain.
In this case, the provider will manage the SPF record for you and you don’t need to add it to your domain SPF record.
Once you’ve gathered the list of IP addresses and include
directives, you should merge all of them in a single SPF record. Remember that you cannot have more than one SPF record, so you can’t skip this step.
For example, if you use both Google Workspace and Mailerlite (a popular email marketing service), the SPF record would look like this:
v=spf1 include:_spf.google include:_spf.mlsend.com ~all
SPF record syntax
All SPF records start with v=spf1
, indicating version 1 of SPF (there are no other versions).
The following parts of the record are called terms, and are usually directives. In the example above, both include:_spf.google.com
and ~all
are directives.
Directives are made of two parts:
- An optional qualifier (the
~
symbol in the example), determining if the directive should generate a success (+
) or failure (-
/~
) SPF result. The qualifier is optional and defaults to+
. - A mechanism (in the example,
include
andall
). Mechanisms define the list of IP addresses or hosts that should be allowed or disallowed, depending on the qualifier.
If a mechanism matches, SPF stops the processing and uses the qualifier to determine the SPF result. If a mechanism doesn’t match, processing continues with the next mechanism.
Let’s go through the most commonly used mechanisms (we’ll see some practical examples later):
Mechanism | Description |
---|---|
a | Matches if the sender IP is found in the list of IP addresses contained in the A or AAAA record of the domain, depending on whether the connection was made over IPv4 or IPv6. Requires a DNS query. |
mx | Same as above but querying the MX record of the domain. |
ip4 ip6 | These mechanisms allow to specify a fixed list of IPv4 or IPv6 addresses, or prefixes/subnets. |
all | Always matches. Usually used at the end of the record. |
include | Evaluates the SPF record of the specified domain name and stops processing only if the SPF result for the referenced record is PASS . If the result is FAIL or SOFTFAIL , processing continues. Requires at least one DNS query. |
If no qualifier is specified, a matching mechanism produces an SPF result of PASS
. For example include:_spf.google.com
is equivalent to +include:_spf.google.com
.
Here are all the qualifiers:
Qualifier | Result |
---|---|
+ | PASS . The client is authorized. |
- | FAIL . The client is not authorized. |
~ | SOFTFAIL . The client is probably not authorized. This result should not cause a rejection but may imply that the message will be subject to «closer scrutiny than normal», as the specification says. |
? | NEUTRAL . No result. The same as not specifying the mechanism, or not setting an SPF record at all. |
Choosing the correct qualifier
The short version is that you should ever only use the ~
(SOFTFAIL
) qualifier when you want to signal a failure.
This is because:
+
is implicitly added in directives when you don’t specify a qualifier.?
doesn’t have any effect.-
isn’t recommended because it can cause unwanted rejections when emails are forwarded.
It is therefore recommended to use ~all
at the end of a SPF record, and not -all
.
This recommendation changed over the years but is now the most commonly shared, especially when DMARC is involved.
For example, the M3AAWG (Messaging, Malware and Mobile Anti-Abuse Working Group) recommends that all domains that send emails should have an SPF record that ends with ~all
, while domains that don’t send emails should have a v=spf1 -all
record.
Reasons to avoid -all
The main reason you should use the ~all
directive instead of -all
mainly relates to forwarding.
Unless Sender Rewrite Scheme is enabled by the forwarding mail server, the Envelope From of the forwarded email is kept as is, causing SPF checks to fail. This is because the IP address of the forwarding server is unlikely to be authorized by your SPF record. (You cannot know which servers will forward your emails, and you obviously cannot allow the whole Internet.)
The risk of an SPF FAIL
is that it might cause an immediate rejection of the email, even before DMARC evaluation takes place. It doesn’t matter that you have a valid and aligned DKIM signature; if the mail server rejects your email as soon as it sees an SPF fail it won’t even look at DKIM.
This is a well-known limitation of how these protocols work together and even has a dedicated section in the DMARC specification:
Some receiver architectures might implement SPF in advance of any DMARC operations. This means that a
-
prefix on a sender’s SPF mechanism, such as-all
, could cause that rejection to go into effect early in handling, causing message rejection before any DMARC processing takes place. Operators choosing to use-all
should be aware of this.
Examples
- For a domain that doesn’t send email:
v=spf1 -all
- To allow a single IPv4 address:
v=spf1 ip4:198.51.100.1 ~all
- To allow a range of IPv4 addresses in CIDR notation:
v=spf1 ip4:198.51.100.1/24 ~all
- To allow everything that
_spf.google.com
(Google Workspace) allows:
v=spf1 include:_spf.google.com ~all
- To allow everything that
spf.protection.outlook.com
(Microsoft 365) allows:
v=spf1 include:spf.protection.outlook.com ~all
- To allow both Google Workspace and Microsoft 365:
v=spf1 include:_spf.google.com include:spf.protection.outlook.com ~all
- To allow Google Workspace but also another IPv4 address:
v=spf1 ip4:198.51.100.1 include:_spf.google.com ~all
- To allow MX and an IPv4 address:
v=spf1 mx ip4:198.51.100.1 ~all
- To allow the MX of another domain:
v=spf=1 mx:example.com ~all
And combinations of these.
Common issues
There are many issues that may cause an SPF record to be invalid and therefore ignored, or that may lead to a processing error:
- Having multiple SPF records is not allowed and causes an immediate SPF error.
- DNS resolution failures generate an error and stop the processing. Make sure that the domains contained in your SPF record exist.
- There’s a limit of 10 total DNS lookups that can be performed during SPF evaluation. This limit includes all DNS queries, including those deriving from the evaluation of an
include
directive. Be careful not to introduce loops in your SPF record. - Having the
all
mechanism in the middle of the record stops processing and prevents further directives from being evaluated, sinceall
always matches. - The DNS record should be a TXT record, not an SPF record (an old record type that has been deprecated for more than a decade).
You can find other common issues and recommendations in this M3AAWG document.