What is An SPF Record and How does It Work: SPF Record Explained
Tags: email, spf
Published: 2021-03-25 02:56:35

An SPF record is a TXT record published in the DNS by the domain owner, which specifies a whitelist of IP addresses allowed to send emails on behalf of the domain. When an email reaches the receiving server, the server will look up the SPF record in the DNS, and perform a check to determine if the email comes from an authorized IP address.

SPF stands for "Sender Policy Framework", which is a mechanism that allows a receiving email server to check if the incoming email is from an IP addresses permitted by a predefined IP address whitelist (SPF record) specified in the DNS (Domain Name System).

For more information on SPF, refer to: [RFC 7208](https://tools.ietf.org/html/rfc7208)

#### SPF record syntax

An SPF record is a single string of text published on the domain in the DNS.

All SPF records start with exactly "v=spf1", followed by a series of "terms". Note that the version part "v=spf1" is mandatory: everything else like "v=spf2" would render the SPF record invalid and cause the receiving server to ignore the record.

Here is an example SPF record:
```text
v=spf1 mx a:example.com/28 -all
```

When published, the above SPF record looks like this in a domain zone file:
```text
example.com.          TXT "v=spf1 mx a:example.com/28 -all"
```

A SPF record must be published as a TXT record, rather than the now deprecated SPF record. Support for the SPF RR (Resource Record) type has been dropped since SPF version 1.

#### SPF Record evaluation

The SPF module returns an authentication result to the receiving server by evaluating the terms in an SPF record.

There are 2 types of terms in SPF: mechanisms and modifiers, which I will explain below.

An SPF authentication result is one of:
* None: either no valid DNS domain name was found, or no SPF record was found on the domain;
* Neutral: it's not stated whether the client IP address is allowed;
* Pass: the client IP address is allowed;
* Fail: the client IP address is not allowed;
* Softfail: the client IP address is probably not allowed;
* Temperror: the SPF module encountered a transient (network) error while performing the check;
* Permerror: the domain’s published records could not be correctly interpreted. This signals an error condition that definitely requires DNS operator intervention to be resolved.

Note that if there is no match when it reaches the end of an SPF record, neutral is returned. This is what SPF record evaluation defaults to.

It's recommended to use either "~all" or "-all" at the end of an SPF record to explicitly indicate what the result should be when no match is found.

#### SPF qualifiers

An SPF qualifier is optionally prepended to an SPF mechanism to denote the result of evaluating that mechanism.

The possible qualifiers, and the results they indicate are:
* "+" pass
* "-" fail
* "~" softfail
* "?" neutral

For example, qualifier "+" indicates pass, while "-" indicates fail, etc.

When no qualifier is prepended to an SPF mechanism, it defaults to "+" (pass).

#### SPF mechanisms

SPF mechanisms are evaluated one by one from left to right.

The result of evaluating an SPF mechanism is one of:
* match; evaluation ends and the qualifier value is returned as the result of evaluating the SPF record;
* not match; moves on to evaluate the next mechanism;
* exception; evaluation ends and the exception value is returned.

Two types of SPF mechanisms are available: basic mechanisms and designated sender mechanisms.

Basic mechanisms are "all" and "include".

Designated sender mechanisms are "a", "mx", "ptr", "ip4", "ip6", and "exists".

Let's explore each one of them below.

##### ip4 mechanism

The ip4 mechanism can be one of:
* **ip4/&lt;ip4-address&gt;**: if the client IP address is the same as ip4-address, it matches;
* **ip4:&lt;ip4-network&gt;/&lt;prefix-length&gt;**: if the client IP address is among ip4-network, it passes; if no prefix-length is provided, /32 is assumed.

For example, this SPF record:
```text
v=spf1 ip4:192.168.0.1/16 -all
```
allows any email sent from any IP address between 192.168.0.1 and 192.168.255.255 to pass SPF authentication.

##### ip6 mechanism

The ip6 mechanism can be one of:
* **ip6/&lt;ip6-address&gt;**: if the client IP address is the same as ip6-address, it matches;
* **ip6:&lt;ip6-network&gt;/&lt;prefix-length&gt;**: if the client IP address is among ip6-network, it passes; if no prefix-length is provided, /128 is assumed.

For example, this SPF record:
```text
v=spf1 ip6:1080::8:800:200C:417A/96 -all
```
allows any email sent from any IP address between 1080::8:800:0000:0000 and 1080::8:800:FFFF:FFFF to pass SPF authentication.

##### all mechanism

The all mechanism matches any IP address. It's often used at the end of an SPF record to provide an explicit result when no match is found for all previous mechanisms. Mechanisms after "all" will never be checked.

For example, in the SPF record below:
```text
v=spf1 mx -all
```
if "mx" doesn't match, "-all" is evaluated and returns a hardfail result as it has a "-" qualifier.

##### include mechanism

The include mechanism makes it possible to allow 3rd-party IP addresses defined in another SPF record to be included in the "master" SPF record.

This is especially useful when you need to use external email delivery/marketing services to send emails for you. To properly authenticate the emails sent from their servers, you must "include" their SPF records in yours. To do so, use the include mechanism on each one of such services.

For example, if you want to use SendGrid to deliver emails for you, you need to include SendGrid's SPF record:
```text
v=spf1 include:sendgrid.net ~all
```
once this is added, all the emails sent from SendGrid servers will pass SPF authentication.

##### a mechanism

The a mechanism matches if the client IP address is one of the IP addresses specified in the mechanism.

The a mechanism can be one of:
* **a**: matches if client IP address is the same as the IP address that the current domain (where the SPF is published on) resolves to;
* **a/&lt;prefix-length&gt;**: matches if client IP address is within the IP range specified by a/&lt;prefix-length&gt;;
* **a:&lt;domain&gt;**: matches if client IP address is the same as the IP address that the specified domain resolves to;
* **a:&lt;domain&gt;/&lt;prefix-length&gt;**: matches if client IP address is within the IP range specified by a:&lt;domain&gt;/&lt;prefix-length&gt;.

For example, suppose this SPF record is published on domain example.com with IP address 199.1.1.1:
```text
v=spf1 a -all
```
now the client server running on IP address 199.1.1.1 sends an email to the receiving server, it will pass SPF authentication when evaluating the a mechanism above.

##### mx mechanism

All the A records for all the MX records for _domain_ are tested. If the client IP is found among them, this mechanism matches.

If _domain_ is not specified, the current domain (where the SPF record is published on) is used.

The mx mechanism can be one of:
* **mx**
* **mx/&lt;prefix-length&gt;**
* **mx:&lt;domain&gt;**
* **mx:&lt;domain&gt;/&lt;prefix-length&gt;**

For example, this SPF record published on example.com:
```text
v=spf1 mx mx:deferrals.domain.com -all
```
allows emails sent from any of:
* A records for all the MX records for example.com
* A records for all the MX records for deferrals.domain.com

##### ptr mechanism

The ptr mechanism can be one of:
* **ptr**
* **ptr:&lt;domain&gt;**

If _domain_ is not specified, the current domain (where the SPF record is published on) is used.

First the hostnames for the client IP are looked up using reverse IP queries. Then SPF tries to find a match using the hostnames as described below.

This mechanism matches if any of the following is true:
* at least one of the A records for a PTR hostname is the same as the original client IP
* a valid hostname ends in _domain_, this mechanism matches.

Invalid hostnames are discarded in this process.

For example, this SPF record is published on example.com, and the client IP address is 199.1.1.1:
```text
v=spf1 ptr -all
```
suppose 199.1.1.1 reverse-resolves to example.com, and example.com resolves to 199.1.1.1, emails from 199.1.1.1 will pass SPF authentication.

Note that you are advised against using the ptr mechanism in your SPF record as it results in many DNS lookups.

##### exists mechanism

The exists mechanism takes a domain parameter. SPF performs an A query on the domain, and if a result is returned, this mechanism matches.

For example, if the SPF record published on example.com is:
```text
v=spf1 exists:microsoft.com -all
```
and microsoft.com resolves to some IP address (no matter what the value is), all emails sent on behalf of example.com pass SPF authentication.

#### SPF modifiers

There are 2 modifiers in SPF: redirect and exp.

SPF modifiers are optional. Any may appear only once per record. Unknown modifiers are ignored.

##### redirect modifier

The redirect modifier takes a domain value:
* **redirect=&lt;domain&gt;**

The SPF record on _domain_ replace the current SPF record.

For example, the SPF record is published on domain.com:
```text
v=spf1 redirect=example.com
```
If there is no SPF record on example.com, the result is unknown. Otherwise, the SPF record on example.com is evaluated and the result of the evaluation is returned.

##### exp modifier

The exp modifier takes a domain value:
* **exp=&lt;domain&gt;**

If the evaluation of an SPF record fails due to a mechanism match like "-all", and the exp modifier is present, an explanation string is returned.

For example, this SPF record is published on example.com:
```text
v=spf1 mx -all exp=explain._spf.%{d}
```
and you've created a TXT record at explain._spf.example.com with this string:
```text
Emails from example.com must only be sent by its own mail servers.
```
When some host other than example.com's mail servers sends an email, which will fail SPF authentication, the above string will be returned.

#### SPF records for email delivery/email marketing services

Here is a list of popular email delivery/email marketing services and their SPF records:

* Google Workspace (Gmail), formerly known as G Suite: _spf.google.com
* SendGrid: sendgrid.net
* Ontraport: _spf-moon-ray.ontramail.com, AND _spf-ontramail.ontramail.com
* Constant Contact: spf.constantcontact.com
* HubSpot: refer to [HubSpot SPF Guide](https://knowledge.hubspot.com/email/do-i-need-to-add-hubspot-to-our-spf-record?target=_blank)
* Campaign Monitor: _spf.createsend.com
* AWeber: send.aweber.com
* Infusionsoft: infusionsoft.com
* ActiveCampaign: emsd1.com
* SendLane: creatensend.com, icmessage.com, slsv1.com, AND slsv2.com
* Mailer Lite: _spf.mlsend.com
* GetResponse: _spf.getresponse.com

To add any of the services above, simply insert an include mechanism for each of its SPF records right before the terminating mechanism of your existing SPF record.

For example, if your existing SPF record looks like this:
```text
v=spf1 a -all
```
and you want to add Ontraport to your SPF record, you need to add both of Ontraport's SPF records right before "-all" so that your SPF record looks like this:
```text
v=spf1 a include:_spf-moon-ray.ontramail.com include:_spf-ontramail.ontramail.com -all
```
Now all emails from Ontraport servers will pass SPF authentication on the receiving servers.

#### SPF record FAQs

**_Can I have multiple SPF records on my domain?_**

Only one SPF record can be published on any particular domain. If multiple SPF records are found, an SPF PermError is returned.

**_What if there are syntax errors in my SPF record?_**

If your SPF record has syntax errors, the receiving server immediately returns a PermError without further record evaluation. This means all your emails will fail SPF authentication and might not reach the inbox.

To check if your SPF record has any syntax errors, use DMARCLY's free [SPF Record Checker](https://dmarcly.com/tools/spf-record-checker).

[Original post](https://dmarcly.com/blog/what-is-an-spf-record-and-how-does-it-work-spf-record-explained)