Two SPF records on one domain: why it fails and how to merge them
A domain can publish exactly one SPF record. Two is a
permerror on its own, whatever the records contain, and every
receiving server treats your SPF as broken until they are merged into one.
Why two records fail
RFC 7208 has a receiver fetch every TXT record at the domain, keep the ones
that begin v=spf1, and return permerror if more than
one is left. There is no rule for choosing between them, so the standard does
not let a receiver try.
That makes this failure different from the ten lookup limit. Each record can be perfectly valid on its own and short enough to cost two lookups; the pair still fails, on every message, before either one is read.
What does and does not count
- Other TXT records are fine. Verification tokens for
Google, Microsoft, Adobe and the rest sit on the same name and are ignored.
Only records beginning
v=spf1count. - Subdomains are separate. A record at
mail.example.comdoes not conflict with one atexample.com. Each name gets its own single record. - Sender ID records do not count. A leftover
spf2.0/prarecord is notv=spf1, so it is not a second SPF record, though it does nothing useful either. - One record split into strings is still one record. A long record published as several quoted strings in one TXT record is correct, and is how anything over 255 characters has to be published.
How it usually happens
Almost always, a vendor's setup instructions said "add this TXT record" and someone did exactly that, next to the record that was already there. A registrar or host publishing its own default record when the domain was created is the other common source.
How to merge them
Take the mechanisms from both, keep one v=spf1 at the front and
one all at the end. So these two:
v=spf1 include:_spf.google.com ~all
v=spf1 include:mailgun.org -all
become one:
v=spf1 include:_spf.google.com include:mailgun.org ~all
Things to watch while you do it:
- Choose the
alldeliberately. If the two records disagree, the merged record can only keep one. For a domain that sends mail and publishes DMARC,~allis the right choice. - Drop duplicates. The same include listed twice costs its lookups twice.
- Turn
redirect=intoinclude:. Aredirect=modifier is ignored whenever the record has anall, so carrying one over as it stands silently drops that sender.include:the same domain before theallinstead. - Mind the join when splitting. A receiver joins the strings of a split record with nothing between them, so the space between two terms has to be inside one of the strings, or two mechanisms run together into one that means nothing.
- Delete the second record. Edit one, remove the other. Adding a third merged record next to the two originals leaves you with three.
Then check the count
Two records that each looked cheap can add up to more than ten DNS lookups
once merged, which trades one permerror for another.
Checking your domain reports two records as its own error rather
than a lookup count, because there is no sensible count until they are merged.
Run it again afterwards to see what the single record costs, and if it is over
the limit, fixing PermError is the next
step.
If you would rather test the merged record before publishing it, the SPF checker accepts a record to evaluate as well as a domain.