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 and all). 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):

MechanismDescription
aMatches 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.
mxSame 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.
allAlways matches. Usually used at the end of the record.
includeEvaluates 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:

QualifierResult
+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, since all 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.