---
title: Will another sender break SPF? How to check an SPF record with the SPF Checker
description: Marketing wants to add one more email tool, and someone has to say whether the SPF record can take another include. To check an SPF record, confirm there is exactly one v=spf1 record, read its ending (~all or -all) and count the DNS lookups against the limit of 10, including the ones hidden inside each include. Here's how, with the free SPF Checker and dig.
canonical: https://watchfor.io/blog/how-to-check-an-spf-record
---

# Will another sender break SPF? How to check an SPF record with the SPF Checker

2026-10-04 · WatchFor Team · Guides · guides, email, deliverability

Marketing wants to add one more email tool, and someone has to say whether the SPF record can take another include. To check an SPF record, confirm there is exactly one v=spf1 record, read its ending (~all or -all) and count the DNS lookups against the limit of 10, including the ones hidden inside each include. Here's how, with the free SPF Checker and dig.

To check an SPF record, look up the TXT records on your domain and make sure
exactly one starts with `v=spf1`. Then read two things: the ending (`-all`
rejects unlisted senders, `~all` marks them suspicious) and the number of DNS
lookups the record triggers. RFC 7208 caps that at 10, counting every
`include`, `a`, `mx`, `ptr`, `exists` and `redirect`, including the ones
nested inside your providers' includes. Go over it and SPF fails with a
`permerror` everywhere.

The free [SPF Checker](/spf-record-checker) fetches and parses the record
from a WatchFor location and counts the lookups it contains. No signup.

## What this tool answers

*Is our SPF record valid, and how close is it to the lookup limit?* Reach
for it before adding a new sending service (a CRM, a helpdesk, a newsletter
tool), when bounces or DMARC reports mention `spf=permerror` or
`spf=softfail`, or after a DNS migration, when TXT records get copied by
hand.

## A real example

### Checking an SPF record before adding one more sender

The request is familiar: "we're starting with a new marketing platform, they
say to add `include:` with their domain to SPF". Before saying yes, check how
much room the record has left. We used a big public domain with a long,
real-world SPF record.

In the [SPF Checker](/spf-record-checker) we typed `github.com` and pressed
**Check SPF**:

![The SPF Checker for github.com: PASS, with the record v=spf1 ip4:192.30.252.0/22 and eight includes (spf.protection.outlook.com, _netblocks.google.com, _netblocks2.google.com, mail.zendesk.com, _spf.salesforce.com, servers.mcsv.net, mktomail.com, sendgrid.net) plus four ip4 entries, policy ~all, DNS lookups 8 / 10 and includes 8](/blog/how-to-check-an-spf-record-result.webp)

The result came back in 123 ms: one valid record, ending in `~all`, with
**8 includes** and **8 / 10** DNS lookups. The count is green, so it looks
like there is room for two more.

There isn't. The checker counts the lookups in the record you publish; it
doesn't open each include. Two of these includes trigger lookups of their
own:

| Include | Lookups inside |
| --- | --- |
| `_spf.salesforce.com` | 1 (`exists:%{i}._spf.mta.salesforce.com`) |
| `sendgrid.net` | 1 (`include:ab.sendgrid.net`) |
| the other six | 0 (they list IP ranges only) |

So a receiver evaluating this record performs **8 + 2 = 10** lookups,
exactly at the limit.

**The answer:** the record is valid today, with no room left. One more
`include:` that contains even a single lookup takes it to 11. Receivers then
return `permerror`, and under DMARC that counts as an SPF fail for all mail
from the domain. Before adding the new tool, free up a lookup: remove a
service you no longer use, or replace a provider's include with the IP
ranges it publishes (only for providers whose ranges rarely change).

## How to read the results

| What you see | What it means |
| --- | --- |
| **PASS / FAIL** | PASS: exactly one `v=spf1` record exists. FAIL: none, or more than one. It doesn't test whether a given server may send. |
| **Record** | The full TXT value. Long records are published as several 255-character strings, which receivers join without spaces; the checker shows them already joined. |
| **Policy** | The `all` at the end: `-all` (fail unlisted senders), `~all` (softfail, usually spam folder), `?all` (neutral, no protection), `+all` (anyone may send, never use). Empty when the record ends in `redirect=`. |
| **DNS lookups** | Lookup terms in the record itself, out of 10. Green up to 8, amber at 9 and 10, red above 10 with "exceeds RFC limit". Nested lookups inside includes are not added; count them as above. |
| **Includes** | How many `include:` terms the record has. |
| **Redirect** | Shown when the record hands off to another domain with `redirect=`. `gmail.com` does this with `redirect=_spf.google.com`. |
| **Notes** | Problems such as "multiple SPF records", which receivers treat as a permanent error. |

## More ways to use it

- **After onboarding a sender.** Run it again once the new include is
  published, and send a test message to check `spf=pass` in the headers.
- **Explaining a softfail.** If a message shows `spf=softfail`, the sending
  IP wasn't covered. Check whether the service that sent it is in the record
  at all.
- **Spotting duplicates after a migration.** Two `v=spf1` records are a
  common leftover when a new provider's setup wizard adds its own. Merge
  them into one.
- **Domains that never send mail.** They should publish `v=spf1 -all`, so
  nobody can send as them. `example.com` does exactly that.

## Tips and gotchas

- **One record, always.** Add new senders to the existing record as extra
  `include:` terms. A second TXT record starting with `v=spf1` breaks SPF
  for the whole domain.
- **`ip4` and `ip6` cost nothing.** Only terms that need a DNS query count
  toward the 10. That's why flattening (listing IP ranges instead of
  includes) frees room, and also why it goes stale when the provider changes
  ranges.
- **Avoid `ptr` and `a`/`mx` you don't need.** Each is a lookup. RFC 7208
  says not to use `ptr` at all.
- **SPF checks the envelope sender, not the From header.** It validates the
  `Return-Path` domain. For DMARC that domain must align with your From
  domain, which is why many providers ask you to set up a custom bounce
  domain. The [DMARC guide](/blog/how-to-check-a-dmarc-record) explains
  alignment.
- **SPF breaks on forwarding.** A forwarded message arrives from the
  forwarder's IP, which isn't in your record. DKIM survives forwarding, so
  sign your mail; see [SPF, DKIM and DMARC explained](/blog/spf-dkim-dmarc-explained).

## Check it from the command line too

Fetch the record, then open each include to count its own lookups:

```bash
dig +short @1.1.1.1 TXT github.com | grep spf1
```

```text
"v=spf1 ip4:192.30.252.0/22 include:spf.protection.outlook.com include:_netblocks.google.com include:_netblocks2.google.com include:mail.zendesk.com include:_spf.salesforce.com include:servers.mcsv.net include:mktomail.com include:sendgrid.net ip4:62.253.2" "27.114 ip4:166.78.69.169 ip4:166.78.69.170 ip4:166.78.71.131 ~all"
```

Note the split in the middle of `ip4:62.253.2" "27.114`: two strings that
receivers join into `ip4:62.253.227.114`. Now the nested lookups:

```bash
dig +short @1.1.1.1 TXT _spf.salesforce.com | grep spf1
dig +short @1.1.1.1 TXT sendgrid.net | grep spf1 | grep -o 'include:[^ "]*'
```

```text
"v=spf1 exists:%{i}._spf.mta.salesforce.com -all"
include:ab.sendgrid.net
```

Repeat for every include in your own record (and for includes inside
includes) and add them up. Anything that ends above 10 needs trimming
before the next sender goes in.

## Keep watching it

SPF breaks when someone adds a sender or deletes the wrong TXT record. An
[email monitor](/email-monitoring) (the email policy type) re-checks the
record on a schedule: **SPF Record Missing** fires by default, and the
optional **SPF Too Many DNS Lookups** rule fires when the estimate passes
10. The estimate is the same top-level count the checker shows, so recount
the nested lookups whenever a provider changes its include.

## FAQ

**How do I check my SPF record?**
Look up your domain's TXT records with an SPF checker or
`dig TXT yourdomain.com`, and find the one starting with `v=spf1`. There must
be exactly one, it should end in `~all` or `-all`, and its lookups must stay
at or under 10.

**What happens if SPF has more than 10 lookups?**
Receivers stop evaluating and return `permerror`. Many treat that like a
failed or missing SPF record, so DMARC can only pass through DKIM, and mail
from senders without aligned DKIM is at risk.

**Should I use ~all or -all?**
`~all` while you're still finding every service that sends as your domain,
`-all` once DMARC reports show nothing legitimate failing. With DMARC at
`quarantine` or `reject`, the DMARC policy decides what happens anyway. To
build a new record, the [SPF and DMARC generator guide](/blog/how-to-create-spf-and-dmarc-records)
walks through it.

---

Canonical page: https://watchfor.io/blog/how-to-check-an-spf-record · All articles: https://watchfor.io/blog/all.md · Site guide: https://watchfor.io/llms.txt
