---
title: "What is SPF? How SPF records work and where they break"
description: "SPF is the DNS record that lists the servers allowed to send mail for your domain. How receivers check it, what each part means and what quietly breaks it."
url: "https://nextmail.sh/blog/what-is-spf"
---

[Blog](https://nextmail.sh/blog.md)[Deliverability](https://nextmail.sh/blog/tags/deliverability.md)

# What is SPF? How SPF records work and where they break

SPF is the DNS record that lists the servers allowed to send mail for your domain. How receivers check it, what each part means and what quietly breaks it.

Arta Nextmail team

October 31, 2026 9 min read

TL;DR

- SPF, the Sender Policy Framework, is one TXT record per domain name that tells receivers which servers may use that name in a message's envelope sender.
- A check that needs more than ten DNS lookups, or finds two SPF records at one name, ends in a permanent error and counts as no pass at all.
- Gmail, Yahoo and Outlook.com require bulk senders to set up SPF and DKIM, and DKIM is what keeps DMARC passing when forwarding breaks SPF.

SPF, the Sender Policy Framework, is how a domain tells receiving mail servers which machines may send mail in its name. It is one TXT record, defined in RFC 7208, and the idea fits in a sentence. The trouble is in the details: SPF checks an address most people never see, and a record can stop working without anyone editing it.

For how it fits with DKIM and DMARC, see [SPF, DKIM and DMARC explained](https://nextmail.sh/blog/spf-dkim-dmarc-explained.md).

## What does SPF check?

Before a message arrives, the sending server gives two names: its own, in the `HELO` or `EHLO` command, and a return address in `MAIL FROM`. That address is the envelope sender, also called the Return-Path, where bounces go.

SPF checks those two names and nothing else. A receiver checks the `MAIL FROM` domain, and RFC 7208 recommends checking `HELO` as well. On a bounce, where `MAIL FROM` is empty, it checks the `HELO` name instead.

The From line people read is not one of them. A message can pass SPF for `bounces.example.net` while its From line says `ceo@example.com`. DMARC closes that gap by counting an SPF pass only when the checked domain aligns with the From domain, which by default means the two share an organisational domain. RFC 9989, the DMARC standard since May 2026, uses only the `MAIL FROM` result, never `HELO`. [What is DMARC?](https://nextmail.sh/blog/what-is-dmarc.md) covers alignment in full.

Receivers usually check before the message itself is transferred. They read the checked domain's record from left to right, and the first mechanism that matches the connecting IP decides the result. If nothing matches, the result is `neutral` unless a `redirect=` takes over.

### Do subdomains need their own SPF record?

Yes. The lookup is for exactly the name in the return address, so a record on `example.com` says nothing about `mail.example.com`. RFC 7208 recommends `v=spf1 -all` on names that send no mail, and `v=spf1 a -all` on each mail server's own host name.

## What does an SPF record look like?

txt

```
example.com.  TXT  "v=spf1 ip4:192.0.2.0/24 ip6:2001:db8::/32 include:_spf.example.net ~all"
```

Every record is a TXT record that starts with `v=spf1`, followed by mechanisms and, conventionally, an `all` at the end. This one authorises two address ranges and every server the record at `_spf.example.net` passes, and soft-fails everyone else.

| Term | Matches | Costs a lookup |
| --- | --- | --- |
| `ip4:192.0.2.0/24` | An IPv4 address or range | No |
| `ip6:2001:db8::/32` | An IPv6 address or range | No |
| `a` | A host name's addresses, the domain's own by default | Yes |
| `mx` | The addresses of a domain's mail servers | Yes |
| `include:_spf.example.net` | Any IP the other domain's record passes | Yes, plus every lookup inside it |
| `exists:%{i}._spf.example.com` | Any IP for which a name built from macros has an A record | Yes |
| `ptr` | An IP whose reverse DNS falls under the domain | Yes |
| `all` | Every IP, so it goes last | No |
| `redirect=_spf.example.com` | Another domain's record, used when nothing matched | Yes |

The qualifier sets the result of a match:

| Qualifier | Result |
| --- | --- |
| `+`, the default | `pass` |
| `-` | `fail` |
| `~` | `softfail` |
| `?` | `neutral` |

### Why `ptr` is discouraged

RFC 7208 marks `ptr` "do not use", because it is slow and loads the reverse-DNS servers under `.arpa`. M3AAWG, the industry's anti-abuse working group, advises against `a` and `mx` too: use your provider's include, and `ip4` and `ip6` for your own servers.

### `include` and `redirect` are not what they sound like

`include` does not copy the other record into yours. The receiver runs a separate check against the included domain, and the include matches only if that check passes, so a `-all` inside it never ends your check. The sharp edge: if the included domain has no SPF record, your whole check is a `permerror`.

`redirect=` is read only when nothing matched, hands over the whole result, and is ignored if the record has an `all` anywhere. It lets many domains share one record, and M3AAWG advises leaving it to advanced users.

## What do SPF results mean?

| Result | What it means |
| --- | --- |
| `pass` | The IP may send for the domain |
| `fail` | The domain says it may not. A receiver that rejects should answer `550 5.7.1` |
| `softfail` | Probably not allowed. RFC 7208 says not to reject on this alone |
| `neutral` | No claim either way, treated exactly like `none` |
| `none` | No SPF record, or no usable domain to check |
| `temperror` | A DNS lookup failed, and a later retry may work |
| `permerror` | The record is broken and has to be fixed in DNS |

SPF never says what to do with a failure. RFC 7208 leaves that to each receiver, and DMARC turns it into an instruction.

## The SPF 10-lookup limit

Each `include`, `a`, `mx`, `ptr`, `exists` and `redirect` costs a DNS lookup, and RFC 7208 caps one check at ten, counting the lookups inside every included record. The eleventh is a `permerror`. M3AAWG warns that one include can cost several, because providers nest records of their own. So a provider adding a lookup to its own record can break yours.

A second limit catches records that point at nothing. A lookup that returns no records, or NXDOMAIN, is a void lookup, and RFC 7208 says receivers should allow only two, so a third is a `permerror` too. Void lookups come from names that do not exist, such as a retired service's or a mistyped one.

Long records hit one more limit. A TXT string holds 255 characters, so longer records are split into quoted strings that receivers join without spaces. End every string but the last with a space, and keep the whole answer under 512 bytes:

txt

```
example.com.  TXT  "v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.0/24 " "ip4:203.0.113.0/24 ~all"
```

To see where your own record stands, the [deliverability checker](https://nextmail.sh/tools/deliverability.md) follows every include and shows how many of the ten lookups you have used.

### What is SPF flattening?

Flattening swaps each include for the `ip4` and `ip6` ranges it resolves to today, so the record costs almost no lookups. RFC 7208 prefers literal addresses where they are stable, but a provider's are not: Google says its outbound mail servers use a large range of addresses that change often. A flattened copy is right on the day you make it, and mail from new addresses fails until someone updates it. Tools that flatten again on a schedule fix that by becoming one more dependency.

Try two cheaper fixes first. Remove includes for services you no longer use: M3AAWG suggests an audit at least once a year. And check which domain each service puts in `MAIL FROM`, because an include on `example.com` only helps mail whose return address is on `example.com`. A service sending from `news.example.com` is checked against that name's record, which has ten lookups of its own.

## Can a domain have two SPF records?

No. A name may have only one TXT record starting with `v=spf1`, and a receiver that finds two returns `permerror`, so neither counts. It happens when a new service's TXT record goes in beside the old one instead of into it.

Merge them into one record with every mechanism from both, one `v=spf1` at the front and one `all` at the end, since anything after the first `all` is ignored. Other TXT records at the same name, such as verification tokens, do no harm.

## Should SPF end in `all` or `-all`?

`-all` says unlisted servers are not authorised, and `~all` says they probably are not. RFC 7208 lets receivers reject on `fail` but not on `softfail` alone, and M3AAWG notes that most receivers treat the two much the same, though some reject more readily under `-all`.

DMARC changed the calculation. RFC 9989 warns that a receiver running SPF first can reject a `-all` failure before it reads the From line. That loses mail DMARC would have passed on an aligned DKIM signature, forwarded mail included, and those rejections never reach your DMARC reports.

So on a domain that sends mail, pair `~all` with DMARC at `quarantine` or `reject`, and keep `-all` for names that never send. Never use `+all`, which authorises the whole internet. And if you must include a platform shared with other senders, RFC 9989 suggests putting `?` in front of it, so the platform's mail gets `neutral` and cannot pass DMARC as you through SPF.

## Why forwarding breaks SPF

A forwarder sends the message on from its own IP address, often with the original `MAIL FROM` intact, so the final receiver finds the forwarder missing from the sender's record and gets `fail` or `softfail`. Three things help:

- **SRS, the Sender Rewriting Scheme.** The forwarder rewrites `MAIL FROM` to an address on its own domain that encodes the original, so SPF is checked against the forwarder and bounces find their way back. Microsoft 365 does this: a message from `bob@example.com` that Alice forwards from `alice@example.net` leaves as `alice+SRS=44ldt=IX=example.com=bob@example.net`. The From line is untouched, so the new pass does not align for DMARC.
- **DKIM.** The signature travels inside the message and survives as long as the signed parts are not changed. RFC 9989 requires it of every domain at `p=reject`. [What is DKIM?](https://nextmail.sh/blog/what-is-dkim.md) explains how signing works.
- **ARC.** Each forwarder seals the authentication results it saw, as RFC 8617 defines, so the last receiver can see that SPF passed at the first hop and decide whether to trust it. Yahoo asks forwarders to implement it.

## Is SPF required? What Gmail, Yahoo and Outlook.com ask for

The standard leaves SPF optional. The largest mailbox providers do not:

| Provider | Every sender | Bulk senders |
| --- | --- | --- |
| Gmail, since February 2024 | SPF or DKIM | Over 5,000 messages a day to Gmail accounts: SPF and DKIM, DMARC of at least `p=none`, and From aligned with one of them |
| Yahoo, since February 2024 | SPF or DKIM | SPF and DKIM, and a DMARC record of at least `p=none` that passes. No threshold is published |
| Outlook.com, since May 2025 | No published rule | Over 5,000 a day to Outlook.com, Hotmail and Live addresses: SPF and DKIM pass, and DMARC of at least `p=none` aligns with one of them |

Gmail began ramping up enforcement in November 2025, with temporary and permanent rejections, and it counts volume by primary domain: cross 5,000 once and you are a bulk sender for good. Outlook.com rejects failing mail from those domains with `550 5.7.515`. Even below the thresholds, Gmail may file unauthenticated mail as spam or reject it.

## How to check your SPF record

Look the record up from a terminal:

txt

```
dig +short TXT example.com
```

Then send yourself a message and find its `Authentication-Results` header:

txt

```
Authentication-Results: mx.example.org; spf=pass smtp.mailfrom=bounce.example.com
```

`smtp.mailfrom` is the domain SPF checked. If it is not yours or a subdomain of yours, the pass belongs to someone else, and DMARC cannot count it for you. The [deliverability checker](https://nextmail.sh/tools/deliverability.md) does the lookup for you, counting through every include, and checks DKIM, DMARC, BIMI and MX in the same pass.

## Where Nextmail fits

When you add a domain to Nextmail, the domain page lists every record to publish, with SPF in two places: on the domain itself, and on its return-path host, `bounce.` under your domain, which gets an MX record too. With the return-path records published, the envelope sender of your mail is on your own domain, so SPF can pass for a name that aligns with your From line.

The page checks each record against public DNS, shows what it found, and names the usual cause when SPF does not match, such as two `v=spf1` records at one name. [Connect a domain](https://nextmail.sh/docs/get-started/domain.md) has the steps and a table of fixes, and [Multiple domains](https://nextmail.sh/docs/knowledge/addresses/domains.md) covers how they are checked.

Incoming forwards are covered too: a message forwarded through Gmail, Microsoft 365, Fastmail or Zoho with an intact ARC chain, whose first hop saw it pass DMARC, is treated as passing DMARC even though SPF broke. The deliverability check also runs [over the API](https://nextmail.sh/docs/api/tools/deliverability.md), for checking domains from your own code.

## Free tools

Try what you just read on your own setup. They are free and need no account.

[Email deliverability checker SPF, DKIM, DMARC, BIMI and MX, read live.](https://nextmail.sh/tools/deliverability.md)

- [Deliverability](https://nextmail.sh/blog/tags/deliverability.md)
- [DNS](https://nextmail.sh/blog/tags/dns.md)
- [Email authentication](https://nextmail.sh/blog/tags/email-authentication.md)

## References

1. 1 [RFC 7208: Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1](https://www.rfc-editor.org/rfc/rfc7208.html) IETF
2. 2 [RFC 9989: Domain-Based Message Authentication, Reporting, and Conformance (DMARC)](https://www.rfc-editor.org/rfc/rfc9989.html) IETF
3. 3 [RFC 8617: The Authenticated Received Chain (ARC) Protocol](https://www.rfc-editor.org/rfc/rfc8617.html) IETF
4. 4 [M3AAWG Best Practices for Managing SPF Records](https://www.m3aawg.org/sites/default/files/doc_files/m3aawg_managing-spf_records-2017-08.pdf) M3AAWG
5. 5 [Email sender guidelines](https://support.google.com/mail/answer/81126) Google
6. 6 [Email sender guidelines FAQ](https://support.google.com/mail/answer/14229414) Google
7. 7 [Google IP address ranges for outbound mail servers](https://knowledge.workspace.google.com/admin/gmail/advanced/google-ip-address-ranges-for-outbound-mail-servers) Google
8. 8 [Sender best practices](https://senders.yahooinc.com/best-practices/) Yahoo
9. 9 [Strengthening Email Ecosystem: Outlook's New Requirements for High-Volume Senders](https://techcommunity.microsoft.com/blog/microsoftdefenderforoffice365blog/strengthening-email-ecosystem-outlook%E2%80%99s-new-requirements-for-high%E2%80%90volume-senders/4399730) Microsoft
10. 10 [Sender Rewriting Scheme (SRS) in Microsoft 365](https://learn.microsoft.com/en-us/exchange/reference/sender-rewriting-scheme) Microsoft

Keep reading

## Related articles

Oct 31, 2026 9 min read

### What is DKIM? How DKIM signatures and records work

DKIM is a signature on every email that proves which domain vouches for it and that nothing changed on the way. How it works, tag by tag, and which keys to use.

Benjamin Deliverability

Oct 31, 2026 9 min read

### What is DMARC? Policy, reports and getting to p=reject

DMARC is a DNS record that checks SPF and DKIM against your visible From domain, tells inboxes what to do with mail that fails and reports who sends as you.

Arta Deliverability

Oct 31, 2026 5 min read

### SPF, DKIM and DMARC explained: how inboxes trust mail

What SPF, DKIM and DMARC each check, why alignment decides whether DMARC passes, and a safe order for moving a domain from p=none to p=reject.

Benjamin Deliverability

Try it yourself

## Your email, on your own domain.

Nextmail puts your domains, sending and inbox in one place, with the full API on every plan, the free one included.

[Open mail](https://nextmail.sh/mail/inbox) [Get Started](https://nextmail.sh/signup.md)[All articles](https://nextmail.sh/blog.md)
