Multiple SPF Records
Multiple SPF records
RFC 7208 is unambiguous: a domain must publish exactly one SPF record. Two or more v=spf1 TXT records at the apex is a hard error. Receivers are not required to pick one — they may return permerror and abandon SPF evaluation entirely, which is the same failure mode as exceeding the lookup limit. SPF effectively does not exist for that domain.
This finding is common on long-lived domains. Two different teams or two different providers each added an SPF record, neither noticed the other, and the domain has been silently failing SPF for months.
How Cathedra detects this
SpfRecordCheck.php pulls every TXT record at the apex and filters for ones starting with v=spf1:
$spfRecords = array_filter(
$txtRecords,
fn ($r) => str_starts_with(trim($r['txt'] ?? ''), 'v=spf1')
);
If the filtered count is greater than one, the multiple-records branch fires.
Cathedra status: error
Severity: high
Finding text (verbatim, with N substituted):
N SPF records found. RFC 7208 requires exactly one. This causes unpredictable evaluation.
Recommendation (verbatim):
Merge all SPF mechanisms into a single TXT record.
The raw_value field on the finding contains an array of the actual record strings. Read those first — the merge decision depends on what’s in each record.
Why this happens
Three patterns produce duplicate SPF records in the wild:
1. A new sender’s setup guide instructed “add a TXT record”
The most common cause. A vendor (SendGrid, Mailgun, Constant Contact, almost any transactional sender) publishes a setup guide that says something like:
Add the following TXT record to your DNS:
v=spf1 include:sendgrid.net ~all
A junior admin reads it, adds the record, and walks away. The existing SPF record stays in place. Now the domain has two SPF records. Both are valid in isolation. The combination is invalid.
This is the easiest case to merge — usually the existing record had -all and the new one is a single include. Drop the standalone record. Add the include: from it into the existing record.
2. Migration left an old record in place
The client moved from on-prem Exchange to M365 (or from Google Workspace to M365, or the reverse). The migration playbook added the new provider’s SPF. Nobody removed the old provider’s SPF. Cathedra reports two records: one for the deprecated provider, one for the active provider.
Confirm with the client which provider is current. Remove the deprecated record entirely. Verify the remaining record covers everything the client actually sends.
3. GoDaddy’s “Auto” or quick-add feature
GoDaddy historically had a one-click “set up email” feature that wrote an SPF record without checking whether one existed. Some legacy domains carry two: one Auto record from the click, one hand-edited record from a tech. Confirm which has the current intent and delete the other.
How to merge two records correctly
You cannot leave both records in place. Merging means combining the mechanisms from both records into one, keeping the strictest all posture, and deleting the second record.
A worked example. Before:
isidoregroup.com. TXT "v=spf1 include:spf.protection.outlook.com -all"
isidoregroup.com. TXT "v=spf1 include:sendgrid.net ~all"
The merge:
- Collect all unique mechanisms from both records. Here:
include:spf.protection.outlook.com,include:sendgrid.net. - Pick the strictest closing rule.
-allis stricter than~all. Keep-all. - Compose a single record.
After:
isidoregroup.com. TXT "v=spf1 include:spf.protection.outlook.com include:sendgrid.net -all"
Confirm the new record stays under the 10-lookup limit. Publish the merged record. Delete the second record entirely. Re-run Cathedra. The multiple-records error should clear and the enforcement check should now read the merged record as -all.
Watch the order of operations
In GoDaddy specifically, the editor lets you save changes to one TXT record at a time. Two safe sequences:
Edit-then-delete (preferred):
- Edit the primary record (the one you want to keep). Add the include from the second record. Save.
- Verify with
dig isidoregroup.com TXT +shortthat the new merged record is live. - Delete the second record.
- Re-run Cathedra.
This pattern is safe because at every step, mail authentication is at least as permissive as it was before. You never have a window where a legitimate sender’s mechanism is missing.
Do not:
- Delete the second record first, then add its mechanism to the primary. There is a window — minutes to hours depending on TTLs — where that sender’s mail authenticates against nothing.
- Edit both records to be identical “to be safe.” Two identical records are still two records. Cathedra still errors.
A subtle edge case — included records can have multiple SPFs too
Sometimes the duplicate isn’t in the client’s record — it’s in a record their SPF includes. If include:somevendor.net resolves to a domain that itself has two v=spf1 TXT records, the receiver’s evaluation of the include returns permerror, which propagates up.
Cathedra does not detect this recursively — its check is at the apex only. If a client’s SPF record looks clean to Cathedra but receivers are still reporting SPF failures, query the records of every include: target manually:
dig spf.protection.outlook.com TXT +short
dig sendgrid.net TXT +short
If any return more than one v=spf1 string, that’s the source. There is no way to fix it client-side — contact the vendor.
What you should remember
- One SPF record per domain. Two records is an RFC violation and a
permerrorat most receivers. - Cathedra reports
error / highwith the exact count: “N SPF records found. RFC 7208 requires exactly one.” - Merge by combining mechanisms, keeping the strictest
all. Edit the primary, delete the second. - After merging, re-check the lookup limit — combined records can push past ten.
- Edit before delete. Never leave a window with missing authorized senders.
Next: the remediation walkthrough. We’ll publish a correct SPF record in GoDaddy from start to finish.
Cathedra alignment: SpfRecordCheck.php — the Multiple SPF Records branch
