SPF Record Checker
Check any domain's SPF record the way receiving mail servers do: every include with its own DNS lookup cost, the running total against the limit of 10, void lookups, and the result for any sending IP, with the term that decided it.
Exactly the name you type: SPF has no inheritance, so mail.example.com needs its own record.
What is an SPF record?
An SPF record is a TXT record that lists the servers allowed to send mail for a domain. It starts with v=spf1, then names senders term by term, and ends with what receivers should make of everyone else (RFC 7208, April 2014, still the current standard).
Receivers check it against the address of the server that connects and the domain in the envelope sender, the MAIL FROM or Return-Path, not the From: line a reader sees. Tying SPF to From: is DMARC's job.
A record covers only the exact name it sits at: SPF has no inheritance, so mail.example.com needs a record of its own. A domain that sends no mail should publish v=spf1 -all.
The include points at _spf.google.com · 8 ranges, no further lookups
ip4:74.125.0.0/16ip4:209.85.128.0/17ip6:2001:4860:4864::/56ip6:2404:6800:4864::/56ip6:2607:f8b0:4864::/56ip6:2800:3f0:4864::/56ip6:2a00:1450:4864::/56ip6:2c0f:fb50:4864::/56~all
v=spf1, first: a record that starts any other way isn't an SPF record.redirect= hands the check to another record; exp= explains a fail.~all. Recorded through Google Public DNS on October 24, 2026.How to check an SPF record
Type a domain above and the checker reads its TXT records, picks the one that starts with v=spf1, then follows every include and redirect the way a receiving server would, counting each DNS lookup as it goes.
By hand, ask for the TXT records: dig TXT example.com +short or nslookup -type=TXT example.com. The answer holds every TXT record at the name; the SPF record is the one starting v=spf1, and a long one comes as several quoted strings that SPF joins with no space. Each include is one more query, and you would have to count them yourself.
To see whether a particular server may send, switch to Test an IP: it runs the same check for that address and shows the term that matched.
dig TXT example.com +shortEvery TXT record at the name, as your resolver has it; the SPF record is the one starting v=spf1. Run it again for each include.
$ dig TXT github.com @dns1.p08.nsone.net +norec +noall +answer github.com. 300 IN TXT "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" # then each include, one query at a time: $ dig +short TXT _spf.salesforce.com "v=spf1 exists:%{i}._spf.mta.salesforce.com -all"
- One record starts with v=spf1. The 23 other TXT records beside it (verification tokens and the like) don't matter to SPF, except for the answer's size.
- 2 strings, joined with no space. The 255-byte split falls inside
ip4:62.253.227.114: receivers join the strings back together, so it's fine. - TTL 5 minutes. How long resolvers may keep this copy after a change. The zone's own server says 5 minutes; a resolver shows what is left of its copy.
- Every include is its own query. Following github.com's record takes 10 lookups, nested ones included. dig shows one name at a time: the count to 10 is yours to add up.
$ nslookup -type=TXT github.com 8.8.8.8
# every TXT record at the name, from Google Public DNS PS> Resolve-DnsName -Name github.com -Type TXT -Server 8.8.8.8
Reading the result: the include tree
The verdict says what receivers get for a sender the record doesn't list: valid with its ending (softfail for ~all, fail for -all), or a permerror with its cause, or "couldn't check" when DNS didn't answer.
The navy column is the lookup budget: one cell per DNS lookup, coloured by the include that spends it, the running total against 10, and void lookups as rings. Past the 10th, the cells turn red and the branch where receivers stop is marked.
The Tree view lists the readings, failures first, each with its RFC section and the fix, then every lookup in the order a receiver asks.

The verdictWhat receivers get for a sender the record doesn't list, and the ending that decides it.The record, term by termEach term coloured by kind; an include carries its owner's mark and its own lookup cost.The readingsOne record, valid syntax, lookups, voids, the ending, ptr, DMARC: failures first, each with its fix.The lookup budgetEvery DNS lookup as a cell, coloured by the include that spends it, against the limit of 10.Each include's costA branch per include: its own cells, what it nests and the running total.The void lookupsRings for queries that came back empty; the third ends the check.The 10-lookup limit and "too many DNS lookups" (permerror)
Receivers count every term that needs a DNS query, include, a, mx, ptr, exists and redirect, through every nested include, and stop at 10. The 11th gives permerror (RFC 7208 §4.6.4). ip4, ip6, all and exp cost nothing.
It's a permerror, not a fail: what a receiver does with it is its own choice, but under DMARC a permerror never counts as a pass. And because the check stops at the first match, a record over the limit still passes for senders listed early and breaks only for those listed late.
The cost hides inside includes: include:_netblocks.mimecast.com spends 9 by itself, Yandex 360's 8, Freshdesk's 7, Mailgun's 5. Google's and Microsoft 365's includes cost 1 each. To get under 10, drop senders you no longer use and move a bulk sender to a subdomain with its own record.
v=spf1 include:spf.protection.outlook.com include:_netblocks.mimecast.com include:mailgun.org include:sendgrid.net -all
Everyone else gets permerror too, not the record's -all fail: receivers stop before they reach it.
- _netblocks.mimecast.comMimecast9 lookups
- _spf.yandex.netYandex 3608 lookups
- email.freshdesk.comFreshdesk7 lookups
- mailgun.orgMailgun5 lookups
- icloud.comiCloud+5 lookups
- spf.titan.emailTitan Email4 lookups
- secureserver.netGoDaddy3 lookups
- _spf.google.comGoogle Workspace1 lookup
- spf.protection.outlook.comMicrosoft 3651 lookup
What are void lookups?
A void lookup is a query that comes back with no records: the name doesn't exist (NXDOMAIN) or exists without the record asked for. Receivers should allow two; the third is permerror (RFC 7208 §4.6.4).
A term pointing at a host that's gone, a:old.example.com, isn't an error by itself: it just doesn't match and counts one void. So does exists:%{i}._spf.mta.salesforce.com for every sender Salesforce doesn't list, which is why the order of includes matters.
A server failure or a timeout is not a void: it's a temperror, and receivers usually try again later.
salesforce.com's own record for 192.0.2.1, recorded October 24, 2026
v=spf1 include:_spf.google.com include:_spf.salesforce.com exists:%{i}._spf.corp.salesforce.com ~all
exists:%{i}._spf.mta.salesforce.com192.0.2.1._spf.mta.salesforce.com → NXDOMAIN · lookup 3 · in _spf.salesforce.comexists:%{i}._spf.corp.salesforce.com192.0.2.1._spf.corp.salesforce.com → NXDOMAIN · lookup 4 · in salesforce.com
Result: softfail, with both allowed voids spent
a:mail.example.com for a name that is still there but no longer has an address: NOERROR with no answer. No match, one void, and the check goes on.NODATAvoidexists:%{i}._spf.mta.salesforce.com builds a name from each sending IP; for an address Salesforce doesn't list, the answer is NXDOMAIN: one void every time.NXDOMAINvoidmx:shop.example.com, a name that is gone, after 2 voids already: receivers stop here.NXDOMAINpermerrorinclude:_spf.vendor.example: the DNS failed, so nothing counts as void.SERVFAILtemperror, not a voidblastmail's include include:spf.bmv.jp starts with two such exists terms: for an address it doesn't list, it spends both allowed voids by itself.
SPF flattening: when it helps and when it breaks
Flattening replaces an include with the addresses it lists, so the record costs fewer lookups. It works for ranges that rarely change, and it backfires when a provider adds a server you never copied: mail from it then fails SPF.
Microsoft says not to flatten include:spf.protection.outlook.com, and Google's and Microsoft's includes already cost one lookup each. Moving a bulk sender to a subdomain such as news.example.com gives it a record, and 10 lookups, of its own.
Before
include:mailgun.org
- Lookups
- 5
- Records fetched
- 5
- Bytes in your record
- 19
- Mailgun changes a range
- picked up at once
After
ip4:159.135.224.0/20 ip4:69.72.32.0/20 ip4:204.220.90.0/23 ip4:204.220.92.0/22 +18 more
- Lookups
- 0
- Bytes in your record
- 450
- TXT strings needed
- 2
- Mailgun changes a range
- your copy is stale
The stale-copy risk. You copy 22 ranges today; when Mailgun adds one, mail from it fails SPF until someone notices and updates the record.
- copied
- Mailgun adds a range
- mail from it fails SPF
~all vs -all (and DMARC)
The last term says what happens to every sender not listed. -all is fail, the strongest statement; ~all is softfail, "probably not"; ?all is neutral, which receivers treat like no SPF at all; +all lets everyone pass and should never be published.
Between ~all and -all there is no wrong answer. Google documents ~all for Google Workspace and Microsoft -all for Microsoft 365. With DMARC at enforcement, ~all lets DMARC decide on DKIM: some receivers reject on -all before DMARC sees a passing DKIM signature, so forwarded mail can bounce (RFC 9989 §7.1).
~all-all?all?all too: harmless there, since only a pass inside an include matches.+all-all with DMARC at p=reject
- SPF fail
- rejected in SMTP
- DKIM never checked
Some receivers act on SPF first, so a forwarded message with a valid DKIM signature can be refused before DMARC runs, and the loss never shows in DMARC reports.
~all with DMARC at p=reject
- SPF softfail
- DMARC checks DKIM
- aligned DKIM: pass
DMARC decides. Without a DKIM signature, though, Microsoft says DMARC is effectively ignored for ~all failures, and recommends -all for that reason.
~all and -all are valid choices; ?all says nothing, and +all lets anyone pass.Common SPF errors and how to fix them
Most broken records come from three slips: a second SPF record added beside the first instead of merging them, an include for a vendor that has since gone, and a value copied from documentation with a typo in it.
Vendor docs print some of these themselves: Mimecast writes –all with an en dash, Postmark include: spf.mtasv.net with a space, Twilio SendGrid lists an aaaa mechanism that doesn't exist. Any syntax error makes the whole record permerror. A trailing dot after a domain, on the other hand, is valid.
Two SPF recordspermerror
v=spf1 include:_spf.google.com ~allv=spf1 include:sendgrid.net ~allFix: merge them into one: one v=spf1, every include once, one ending.
More than 10 lookupspermerror
v=spf1 include:_netblocks.mimecast.com include:mailgun.org -all14 lookups
Fix: drop senders you no longer use, or give a bulk sender a subdomain of its own.
A third void lookuppermerror
v=spf1 a:old.example.com mx:gone.example.net exists:%{i}.spf.example.org -all3 voids
Fix: delete terms whose names are gone, and put exists includes last.
An include with no SPF record behind itpermerror
v=spf1 include:spf.retired-vendor.example -allFix: correct the name, or remove the vendor you left.
An en dash in –allpermerror
v=spf1 include:_netblocks.mimecast.com –allPrinted this way in Mimecast's own documentation
Fix: a plain hyphen: -all.
A space after include:permerror
v=spf1 a mx include: spf.mtasv.net ~allPrinted this way in Postmark's own documentation
Fix: include:spf.mtasv.net
An aaaa mechanismpermerror
v=spf1 aaaa:example.com ~allPrinted this way in Twilio SendGrid's own documentation
Fix: a:example.com; a already matches the AAAA records of IPv6 senders.
A trailing dotvalid
v=spf1 include:spf.protection.outlook.com. -allValid: RFC 7208 §7.3 says not to publish it, but receivers must accept it.
Zendesk's help also says “redirect: syntax is supported”: the modifier is written redirect= (RFC 7208 §6.1).
What Gmail, Yahoo and Microsoft require
Since February 2024 Gmail and Yahoo require every sender to set up SPF or DKIM. Bulk senders need both, a DMARC record of at least p=none, and a From: domain aligned with one of them. Outlook.com asks high-volume senders for SPF and DKIM both, and has rejected those without them since May 5, 2025.
Gmail's threshold is more than 5,000 messages a day to personal accounts; Yahoo publishes none. SPF alone never makes mail "aligned": the MAIL FROM domain has to be yours, not the provider's bounce domain.
| Gmail | Yahoo | Outlook.com | |
|---|---|---|---|
| Bulk threshold | Gmailmore than 5,000 messages a day to personal Gmail accounts, counted per primary domain | Yahoonone published: “We will not specify a volume threshold” | Outlook.commore than 5,000 a day (Microsoft's blog); 5,000 or more from one From: domain (its support article) |
| All senders | GmailSPF or DKIM, plus forward and reverse DNS, TLS and a spam rate under 0.3% | YahooSPF or DKIM, plus forward and reverse DNS and a spam rate under 0.3% | Outlook.comno hard rule |
| Bulk senders | GmailSPF and DKIM, DMARC (p=none is enough), From: aligned with SPF or DKIM, one-click unsubscribe for marketing mail | YahooSPF and DKIM, DMARC at least p=none and passing, From: aligned with SPF or DKIM, one-click unsubscribe | Outlook.comSPF must pass and DKIM must pass, DMARC at least p=none, aligned with SPF or DKIM |
| In force since | GmailFebruary 1, 2024; rejections ramped up from November 2025 | YahooFebruary 2024 | Outlook.comMay 5, 2025 (rejection) |
| SPF failure shows as | Gmail4.7.275.7.27 | Yahoonot documented for SPF | Outlook.com550 5.7.515 |
Japan: NTT docomo's strictest setting, 「全て拒否する」, refuses mail whose sending IP the domain's SPF record doesn't authorise, and mail from domains that publish no SPF record at all. docomo.ne.jp
Sourcessupport.google.comsenders.yahooinc.comtechcommunity.microsoft.com
SPF lookup vs dig and nslookup
dig and nslookup show the TXT records as your resolver has them: the SPF record and whatever else sits at the name. They don't follow includes, count lookups or voids, or say what a receiver would decide.
An SPF lookup like this one walks the whole record, so the numbers match what a receiving server computes. Use dig for one name, this checker for the verdict.
dig TXT | nslookup -type=TXT | ||
|---|---|---|---|
| Shows the TXT records | This checkerYessplit into terms | dig TXTYes | nslookup -type=TXTYes |
| Joins a long record's strings | This checkerYesas receivers do | dig TXTNoshows each string apart | nslookup -type=TXTNoshows each string apart |
| Follows every include | This checkerYesthe whole tree | dig TXTNoone name per query | nslookup -type=TXTNoone name per query |
| Counts lookups to 10 | This checkerYesa running total, per include | dig TXTNoby hand | nslookup -type=TXTNoby hand |
| Counts void lookups | This checkerYeswith the names | dig TXTNo | nslookup -type=TXTNo |
| Says two records = permerror | This checkerYes | dig TXTNoprints both | nslookup -type=TXTNoprints both |
| Tests a sending IP | This checkerYescheck_host and the matching term | dig TXTNo | nslookup -type=TXTNo |
| Asks your own resolver | This checkerNoGoogle Public DNS, from our server | dig TXTYes | nslookup -type=TXTYes |
SPF checker: frequently asked questions
What is an SPF record check?
How do I test if an IP address is allowed to send email for my domain?
What does "SPF too many DNS lookups" mean?
What is an SPF permerror?
What is a void lookup in SPF?
Should my SPF record end with ~all or -all?
Can a domain have two SPF records?
What is SPF flattening, and should I use it?
Does a subdomain need its own SPF record?
Why does SPF pass here but fail at Gmail?
What is the difference between SPF, DKIM and DMARC?
How long does an SPF record change take to work?
Missing something?Suggest a feature or a new tool
Related email and DNS tools
MX Lookup
Find mail exchange records and mail server details
DMARC Checker
Check any domain's DMARC policy the way receivers read it, with reports, SPF and DKIM
DKIM Checker
Find a domain's DKIM keys across 140 common selectors, or verify an email's signature
Email Header Analyzer
Parse raw email headers and trace the email journey
IP Blacklist Check
Check if an IP address is listed on spam or malware blacklists
Reverse DNS Lookup
Find hostnames associated with any IP address via PTR records
DNS Lookup
Every DNS record from the domain's own nameservers, beside Google's and Cloudflare's cache
SPF Record Generator
Build a valid SPF TXT record for your email providers and servers, with a live check of the 10 DNS lookup limit
