A domain may publish only one SPF record. With two or more, receivers return permerror and SPF passes for none of your mail, including mail from servers both records list. See every v=spf1 record your domain publishes below, get one merged record with its DNS lookup count, and check the fix with dig or nslookup.
Last verified 1 October 2026.
SPF lives in a TXT record on the domain itself (example.com) and starts with v=spf1. A receiving server fetches every TXT record at that name, keeps those that begin with v=spf1, and needs exactly one. RFC 7208, the SPF standard, is explicit:
Receivers don’t pick one or combine them. Microsoft’s SPF documentation says multiple SPF records “cause SPF to return permerror (the receiving system can’t determine which record to evaluate)”. A permerror is not a pass, so DMARC can only pass on an aligned DKIM signature, and mail without one fails DMARC and is handled by your p= policy. Gmail, Yahoo and Outlook.com all require SPF from bulk senders (see the bulk sender requirements), and Outlook.com’s 550 5.7.515 bounce is a common symptom.
Sources: RFC 7208 §3.2, §3.3, §4.5 and §6.1; Microsoft Learn, Set up SPF to identify valid email sources for your Microsoft 365 domain; Google Workspace Admin Help, Set up SPF (checked 1 October 2026).
Enter the domain in your From: address (or the envelope sender, if it differs). The lookup runs in your browser against public DNS, lists every TXT record that starts with v=spf1 plus near misses receivers ignore, and builds one merged record with its lookup count.
Suggested single record (replace every record above with this one; read the notes first):
Longer than 255 characters. Most DNS hosts split it for you; if yours asks for quoted strings, enter it as one TXT record with these strings (each ends in a space, because receivers join them with none added):
This reads what your DNS publishes right now through Cloudflare’s public resolver, or Google’s when Cloudflare’s fails. If you just changed a record, an old answer can stay cached until its TTL runs out. For the full A–F grade with DMARC, DKIM, MTA-STS, BIMI and DNSSEC, use the DMARC checker.
Two TXT records on the domain that both start with v=spf1 | Multiple records: permerror. Even when the two are identical. The version tag is case-insensitive, so V=SPF1 counts too. |
|---|---|
| One TXT record made of several quoted strings | Fine. Receivers join the strings with no space added (RFC 7208 §3.3): "v=spf1 include:_spf.google.com " "~all" is one record. Keep a space at the end of a string or the start of the next, or two terms run together. |
v=spf10, spf2.0/pra, or a record with a space before v=spf1 | Not SPF. The record must begin with exactly v=spf1 followed by a space or the end of the record (§4.5), so these are discarded and don’t count. They don’t authorize anything either. |
One v=spf1 record plus verification tokens (google-site-verification=…, MS=…) | Fine. Other TXT records are ignored by SPF. |
One record on example.com and one on mail.example.com | Fine. Different names, and each sending name needs its own. |
The most common cause. You start sending through a marketing tool, CRM, help desk or booking system, and its setup guide says “add this TXT record: v=spf1 include:… ~all”. Adding it as a new record next to the one your mail provider needs creates the duplicate. The fix is to add the new include: to the existing record instead.
Moving from one mail provider to another and adding the new provider’s record without removing the old one. Some web hosts and registrars also publish a default SPF record (often v=spf1 a mx ~all) that sits next to the one you add later.
A long record entered as two separate TXT records instead of one record with two strings. If both halves start with v=spf1, that’s the error; if only the first does, the second half is silently ignored and the servers in it fail SPF.
Say example.com uses Microsoft 365 and a booking system that told you to add its own record:
v=spf1 include:spf.protection.outlook.com ip4:203.0.113.10 -all
v=spf1 include:spf.protection.outlook.com include:_spf.booking.example -all
Replace both with one:
v=spf1 ip4:203.0.113.10 include:spf.protection.outlook.com include:_spf.booking.example -all
v=spf1, once.ip4: and ip6: ranges first (they cost no DNS lookups), then a, mx and include:. Drop exact duplicates.all. If the records agree, keep theirs. If one says -all and another ~all, use ~all until your DMARC reports show every legitimate sender passing, then move to -all. Never use +all: it authorizes every server on the internet.redirect= into include:. A record with an all mechanism ignores redirect= (§6.1), so redirect=_spf.example.net becomes include:_spf.example.net. Keep at most one exp=.include, a, mx, ptr and exists counts, including those inside included records, and the limit is 10. Two records that were each under the limit can be over it once merged: see how to fix “too many DNS lookups”.include:_spf.google.com. Google’s setup page gives combined records for common pairs, for example v=spf1 include:_spf.google.com include:spf.protection.outlook.com ~all for Google Workspace with Microsoft 365.include:spf.protection.outlook.com. Microsoft says most Microsoft 365 organizations need it, and that only one SPF record is allowed per domain or subdomain.include: (or ip4:) term from its setup guide and add it to your record, not the whole v=spf1 … ~all line.From a terminal on macOS or Linux:
dig +short TXT example.com | grep -i 'v=spf1'
You want exactly one line. One line with several quoted parts is one record split into strings, which is fine. Two lines is the error. On Windows:
nslookup -type=TXT example.com
Each record appears as its own text = entry; exactly one should start with v=spf1. To skip your local cache:
dig +short TXT example.com @1.1.1.1
dig +short TXT example.com @8.8.8.8
If the old record still shows, the resolver is serving a cached answer: wait for its TTL to expire and check again, or run the checker above. Then send a test message to a Gmail address and look for spf=pass in “Show original”.
No. A domain can publish exactly one TXT record that starts with v=spf1. To authorize a second provider, add its include: term to the existing record, for example v=spf1 include:_spf.google.com include:spf.protection.outlook.com ~all.
No. One TXT record can hold several strings, and receivers join them with no space added (RFC 7208 §3.3). It is still one record. Make sure a space sits at the end of one string or the start of the next, or two terms run together.
The merged record needs the lookups of both records combined, minus any include, a or mx term they shared. If that comes to more than 10, the merged record is a permerror too, so check the count before you publish. The checker above shows it.
-all or ~all?If both records ended the same way, keep it. If they differed, start with ~all: while the duplicates were live no record was being applied, and -all could reject mail from a sender you haven’t tested. Move to -all once DMARC reports show all your legitimate mail passing SPF.
A subdomain that sends mail needs its own record, because SPF is checked on the exact domain in the envelope sender. The one-record rule applies per name: example.com and mail.example.com can each have one v=spf1 record.
If you manage domains for clients or brands, Email Security Signals checks a whole list and returns one row per domain with SPF, DKIM, DMARC and an A–F grade. Duplicate SPF records show up as a high-severity spf_multiple_records issue, with the count in spf.recordCount, ready to filter in a CSV export. $0.02 per graded domain ($20 per 1,000); invalid inputs, domains that don’t exist, DNS lookup failures and duplicates are free.
Related: fixing SPF “too many DNS lookups”, fixing “multiple DMARC records found” and fixing Outlook’s 550 5.7.515 bounce.