Why two SPF records are worse than none
Publishing a second SPF TXT record makes every check a permerror. How it happens, how to detect it with dig, and how to merge records safely.

On this page(7 sections)
A domain with no SPF record gets a neutral result. A domain with two SPF records gets a permanent error on every single check.
Of all the ways to break email authentication, this one is the easiest to cause and the most expensive to ignore.
Why two records break everything
RFC 7208 is explicit: when a receiver queries TXT records for a domain and finds more than one that begins with v=spf1, the SPF result is permerror. The receiver does not try to merge them, pick the first, or pick the one that matches. It stops.
A permerror means the domain's SPF policy cannot be evaluated. For DMARC purposes it counts as SPF not passing. If your DKIM signatures are aligned, DMARC may still pass. If any of your mail streams depend on SPF alone, they now fail authentication everywhere, and some receivers will reject them outright.
Compare that with publishing nothing. No SPF record gives a none result, which receivers handle as an absence of information. Two records is strictly worse than zero.
How it happens
Nobody sets out to publish two SPF records. It usually happens in one of these ways:
- A vendor's setup guide says "add this TXT record." Someone pastes
v=spf1 include:spf.vendor.example ~allas a new record instead of adding the include to the existing one. - A DNS migration duplicates records. Moving zones between providers, an import tool copies the old record while someone also creates a new one by hand.
- Two teams own DNS. Marketing adds their platform; engineering adds the transactional provider; neither looks at what is already there.
- A registrar adds a default. Some registrars and hosting panels create an SPF record automatically when you enable their mail product.
- Leftover SPF RR type records. RFC 4408 defined a dedicated SPF record type (type 99). RFC 7208 deprecated it, and receivers should query TXT. A stale type-99 record does not cause a permerror on its own, but it is a sign that the zone has not been reviewed in a long time and is worth cleaning up.
Detecting it
Query all TXT records on the domain and look for more than one that starts with v=spf1:
dig +short TXT example.com
A broken result looks like this:
"google-site-verification=abc123..."
"v=spf1 include:_spf.workspace.example ~all"
"v=spf1 include:spf.sender.example ~all"
Other TXT records (domain verification tokens, for example) are fine. Only records beginning with v=spf1 count, and the match is case-insensitive.
You can also see the effect in message headers. A receiver's Authentication-Results header will show something like spf=permerror with a reason mentioning multiple records. DMARC aggregate reports will show SPF results of permerror across every source.
Check each subdomain you send from as well. bounce.example.com and example.com are separate names with separate records, and each can only have one.
Merging records correctly
The fix is a single record that contains every mechanism from both, with one all at the end.
Two broken records:
v=spf1 include:_spf.workspace.example ip4:192.0.2.10 ~all
v=spf1 include:spf.sender.example -all
One merged record:
v=spf1 include:_spf.workspace.example include:spf.sender.example ip4:192.0.2.10 ~all
Work through these steps when merging:
- Collect every mechanism from each record, removing exact duplicates.
- Pick one
allqualifier. If the two records disagree (~allversus-all), choose deliberately. While you are unsure that every sender is listed,~allis the safer choice. - Put
alllast. Anything after it is ignored. - Recount lookups. Merging two records that were each within limits can push the combined record over ten DNS-querying terms. Count includes recursively before publishing.
- Check for
redirect=. A record withredirect=hands evaluation to another domain when nothing matches. It cannot meaningfully coexist with anallmechanism, sinceallalways matches first. If one of the records usedredirect=, decide which model you want. - Delete the old records in the same change window you publish the new one.
Publishing the change safely
The order of operations matters a little, because resolvers cache records for the TTL.
- If possible, lower the TTL on the existing records first and wait for the old TTL to expire.
- Publish the merged record and delete the duplicates in one edit, if your DNS provider supports atomic changes.
- If it does not, delete one duplicate first. A brief window with one incomplete record is better than any window with two.
- Verify from an outside resolver, not just your provider's dashboard:
dig +short TXT example.com @1.1.1.1
dig +short TXT example.com @8.8.8.8
Then send a test message to a mailbox you control at a major provider and read the Authentication-Results header to confirm spf=pass.
Preventing it from happening again
The root cause is almost always process, not knowledge. A few habits help:
- Keep DNS in version control. Whether you use infrastructure-as-code or simply a reviewed zone file, a pull request makes a second
v=spf1line visible to a reviewer. - Add a CI check. A tiny script that fails if any name has more than one TXT value starting with
v=spf1catches the mistake before it ships. - Name an owner for email DNS. One person or team approves changes to SPF, DKIM and DMARC records.
- Rewrite vendor instructions. When a vendor says "add this TXT record," translate that internally to "add this include to our existing SPF record."
- Watch DMARC reports. A sudden jump in SPF permerrors across all sources is the signature of a duplicate record.
Here is a minimal check you can run in CI or a cron job:
for name in example.com bounce.example.com news.example.com; do
n=$(dig +short TXT "$name" | grep -ci '"v=spf1')
if [ "$n" -gt 1 ]; then
echo "ERROR: $name has $n SPF records"; exit 1
fi
done
Key takeaways
- More than one
v=spf1TXT record on the same name is a permerror for every message, worse than having no record. - It usually comes from vendor instructions taken literally, DNS migrations or uncoordinated teams.
- Merge into one record with every mechanism and a single
allat the end, then recount DNS lookups. - Verify from public resolvers and a real test message.
- Version control and a simple automated check stop it from recurring.
Start with Koltrix
Your domain, one inbox, and an API that sends.
A team inbox where AI sorts and drafts (nothing is sent without your click), plus the transactional API and SMTP relay your product sends with. 7 days free, no card.

