# SPF record

An SPF (Sender Policy Framework) record lists the servers allowed to send email using your domain. Receiving servers check the sending server's IP address against it, and mail from anywhere else can be marked as spam or rejected. It's a TXT record at the domain that starts with v=spf1.

- Record type: TXT (SPF is carried in TXT records)
- Lives at: @ (the domain that sends)
- Used for: Email
- How common: Everyday
- Defined in: RFC 7208

## Format

```
@ <ttl> IN TXT "v=spf1 <mechanisms> <all>"
```

| Field | What it means | Accepted values |
| --- | --- | --- |
| v=spf1 | Marks the TXT record as SPF. Must come first, exactly like this. | `v=spf1` |
| include: | Allow everything another domain's SPF allows. How you add Google, Microsoft, Mailchimp and other senders. | `include:_spf.google.com`. Costs one DNS lookup, plus the lookups inside it. |
| ip4: and ip6: | Allow a specific address or range. | `ip4:192.0.2.10`, `ip4:192.0.2.0/24`, `ip6:2001:db8::/32`. No lookups. |
| a and mx | Allow the domain's own web server addresses, or its own mail servers. | `a`, `mx`, or `a:other.example`. One lookup each. |
| exists:, ptr | Advanced (exists) and retired (ptr) mechanisms. | Avoid ptr; it's slow and discouraged. |
| redirect= | Use another domain's SPF record instead of this one. | `redirect=_spf.example.com`. Only used when there's no all. |
| Qualifier | A prefix on any mechanism: + pass (default), - fail, ~ softfail, ? neutral. | `+`, `-`, `~`, `?` |
| all | What happens to everything not listed. Goes last. | `-all` reject, `~all` mark as suspicious, `?all` no opinion. Never `+all`. |

## Examples

### Google Workspace only

```
@ 3600 IN TXT "v=spf1 include:_spf.google.com ~all"
```

Google's servers may send; anything else is softfailed.

### Microsoft 365 plus a newsletter tool

```
@ 3600 IN TXT "v=spf1 include:spf.protection.outlook.com include:servers.mcsv.net -all"
```

Two senders, everything else fails.

### Your own server

```
@ 3600 IN TXT "v=spf1 ip4:192.0.2.25 -all"
```

No lookups needed at all.

### A domain that never sends email

```
@ 3600 IN TXT "v=spf1 -all"
```

Nobody may send as this domain. Use it on every domain you own that doesn't send mail, together with DMARC p=reject.

## The 10 lookup limit

Checking an SPF record may take at most 10 DNS lookups, counting every include, a, mx, ptr, exists and redirect, including the ones inside included records. Go over and SPF returns a permanent error, which most receivers treat as a fail. Big providers' includes cost several lookups each, so four or five senders can be enough to break it.

The builder above counts the lookups live. To get under the limit: remove senders you no longer use, replace a and mx with ip4 ranges, or move some senders to a subdomain with its own SPF (such as news.example.com for newsletters).

## ~all or -all?

-all asks receivers to reject unlisted senders; ~all asks them to accept but mark the mail. In practice large receivers use SPF as one signal and leave the final decision to [DMARC](https://lemon.domains/dns-records/dmarc), so ~all with a DMARC policy is a solid, common setup. Use -all on domains that never send mail.

## The SPF record type

There was once a separate SPF record type (number 99). It was retired in 2014; receivers only read SPF from TXT records. If your DNS host still offers the SPF type, don't use it.

## TTL

3600. While changing senders, 300 lets you fix mistakes quickly.

## Common mistakes

- Two SPF records at the same name. Receivers return an error; merge them into one.
- Going over 10 DNS lookups by adding include after include.
- Ending with +all, which allows the whole internet to send as you.
- Forgetting a sender, such as your invoicing tool or help desk, so its mail lands in spam.
- Putting SPF on www or another name that doesn't send. It belongs on the domain in the From address (usually @).
- Typing spf1 without v=, or v=spf2.0 (that's the retired Sender ID).

## At your DNS host

- Add it as a TXT record at @. Edit the existing SPF record rather than adding a second one.
- Route 53: wrap the value in double quotes.

## Questions

### Can I have two SPF records?

No. A domain must have exactly one TXT record starting with v=spf1. Combine all senders into one record with several include: entries.

### What's the difference between ~all and -all?

-all asks receivers to reject mail from unlisted servers; ~all asks them to accept it but treat it as suspicious. With a DMARC policy in place, both work well; use -all on domains that never send.

### Does SPF stop people spoofing my From address?

Not on its own. SPF checks the hidden envelope sender, not the From address people see. DMARC ties SPF and DKIM to the visible From address, which is what stops spoofing.

### Why does my SPF fail with too many lookups?

Each include, a, mx, ptr, exists and redirect costs a lookup, including those inside included records, and the total may not exceed 10. Remove unused senders or replace some with ip4 ranges.

## Related

- [DMARC record](https://lemon.domains/dns-records/dmarc.md)
- [DKIM record](https://lemon.domains/dns-records/dkim.md)
- [TXT record](https://lemon.domains/dns-records/txt.md)
- [MX record](https://lemon.domains/dns-records/mx.md)

Interactive version with a record checker and live lookup: https://lemon.domains/dns-records/spf
