Blog · Spf

Can a Domain Have Two SPF Records? What Happens When You Add a Second One

Can a Domain Have Two SPF Records? What Happens When You Add a Second One

No. A domain publishes exactly one SPF record. A second TXT record starting with v=spf1 does not add to the first one. It makes SPF evaluation undefined for every sender on that domain, and the result the standard specifies is permerror.

The part that trips people up is what happens next, or rather what does not happen. Often nothing visible happens. A receiver that keeps evaluating the first record still returns Pass. DMARC still passes because DKIM is aligned. You find out months later, usually when a DKIM key rotates or a stricter receiver starts enforcing, and by then there is no SPF fallback left.

This post covers the rule, why the damage stays hidden, how to check your domain in under a minute, how to merge the records safely, and how to stop the next vendor from adding a third.

If you would rather not run commands, DMARCFlow's SPF Checker (https://dmarcflow.com/spf-checker) does this check in the browser, needs no account, and lists "Multiple SPF records on the same domain" as a named issue with the fix attached.

The short answer: one SPF record per domain

| Situation | Result |
|---|---|
| One v=spf1 record | Normal evaluation |
| Two or more v=spf1 records | permerror (RFC 7208, Section 4.5) |
| One record carried in several strings | Legal, DNS concatenates it |
| No SPF record at all | none |

Three things follow from that table:

  • One record per DNS name. example.com and mail.example.com each hold their own record, and those do not conflict.
  • The fix is merging, never adding. Both sending services belong inside the same record.
  • The moment to check is the moment someone connects a new tool, not the day deliverability drops. If a setup screen hands you a complete v=spf1 record, ask for the include: fragment instead and merge that into what you already publish.

What the SPF standard actually requires

RFC 7208 turns multiple records into permerror

RFC 7208, Section 3.2: "A domain name MUST NOT have multiple records that would cause an authorization check to select more than one record."

Section 4.5 spells out the consequence. A receiver fetches the TXT records for the domain, discards everything that does not begin with exactly v=spf1, and then: "If the resultant record set includes no records, check_host() produces the 'none' result. If the resultant record set includes more than one record, check_host() produces the 'permerror' result."

Two details are worth keeping.

Only records that start with v=spf1 are counted. Your DKIM keys and your Google verification records are not part of this problem. A record that starts with v=spf10 does not match either and is discarded.

The rule applies per DNS name. A subdomain record stands on its own, so it does not conflict with the record on the parent domain. A subdomain with its own record is the clean way to hand one provider its own sending identity.

One more piece of housekeeping. RFC 7208, from April 2014, obsoleted RFC 4408. You will still see the older number cited in vendor guides and forum answers. The rule did not change, but 7208 is the current document.

What permerror means at the receiving server

Section 8.7 defines the result: a permerror means the domain's published records "could not be correctly interpreted. This signals an error condition that definitely requires DNS operator intervention to be resolved."

When a receiver rejects on that basis, RFC 7208 says it should use SMTP reply code 550 with the enhanced status code 5.5.2. That is what a hard rejection looks like in the mail logs.

Here is the part most explainers skip. Appendix G.3 says the decision about what to do with a permerror belongs to the receiver. Rejection is one option, not a requirement. A receiver can also accept the message and move on.

That is the whole reason this problem is underdiagnosed.

One record split into several strings is not the same thing

A single TXT record can be published as several quoted strings. DNS concatenates them into one value, RFC 7208 Section 3.3 covers it, and it is legal.

The illegal case is two separate TXT resource records that each start with v=spf1.


One record. Legal.

example.com. TXT "v=spf1 include:_spf.google.com include:sendgrid.net ~all"

Two records. This is the problem.

example.com. TXT "v=spf1 include:_spf.google.com ~all" example.com. TXT "v=spf1 include:sendgrid.net ~all"

Some DNS panels display a long record across several lines. That is still one record. The way to tell them apart is to count the records the DNS returns, not the lines the value is wrapped into.

Why a second record rarely breaks anything you can see

Receivers are not uniform

There are field reports of the duplicate staying completely invisible. One sysadmin reported a domain with two v=spf1 records where the envelope sender was the root domain itself, and every DMARC aggregate report showed SPF, DKIM and DMARC passing at a Microsoft 365 receiver. The receiver evaluated the first record, the sending IP matched, and the result was Pass.

Treat that as a field report, not as spec. The spec result is permerror. But if you have watched SPF pass on a domain you know has two records, that is not a contradiction, and it is not evidence that the setup is fine.

A duplicate SPF record is a latent failure, not a visible one. That is what makes it expensive.

Why aligned DKIM hides the problem

DMARC passes when an authenticated identifier aligns with the From domain. RFC 7489, Section 6.6.2, step 5: "If one or more of the Authenticated Identifiers align with the RFC5322.From domain, the message is considered to pass the DMARC mechanism check."

SPF and DKIM are two doors. DMARC needs only one of them to be open.

So with healthy, aligned DKIM, a duplicate SPF record costs you your SPF support and nothing else. Mail keeps flowing, DMARC keeps passing, and the report data still looks clean.

When the SPF fallback disappears

The fallback is what breaks later:

  • A DKIM key rotation goes wrong and messages stop verifying.
  • A new sending source appears that DKIM does not cover. A CRM, a helpdesk, a marketing platform.
  • Mail is forwarded. Forwarded mail fails SPF by design.
  • A receiver gets stricter and starts enforcing the permerror instead of tolerating it.
  • The original sending path is retired, so DKIM no longer matches what you actually send.

Any one of those, and the SPF support you assumed you had is gone. Order confirmations and password resets start landing in spam, and it looks like a brand new problem rather than a duplicate that has been sitting in the zone for a year.

Which mail breaks first

The mail that leans on SPF instead of DKIM. In practice that is order confirmations, shipping updates, password resets and account notifications. These are the messages a customer acts on, and the ones nobody monitors.

Where the second record comes from

The four usual sources

1. A new tool says "add this SPF record". Newsletter service, CRM, helpdesk, transactional provider. The setup screen hands you a complete record, not an include fragment to paste into the one you have.
2. The hosting provider adds one. One webhosting business reported losing $50,000 in ARR after a second SPF record appeared in their GoDaddy hosted zone twice in a row, with no notification and no change ticket. When the third party is the registrar, nobody tells you.
3. An agency or a freelancer. A migration where the old include was left in place and the new provider's record was added next to it instead of merged into it.
4. A migration that leaves the old record behind. Moving to a new ESP, moving from Google Workspace to Microsoft 365, adding a second transactional provider.

Why nobody catches it

  • The new tool's setup check looks only for its own include, so it shows green.
  • The DNS panel shows two TXT entries and neither one looks wrong. No error on the site, no failed checkout, no alert.
  • The damage lands on the mail nobody knew was relying on SPF.

How to check whether your domain has one

Check 1: count the records with dig


dig TXT example.com +short

You are looking for lines that start with v=spf1. Exactly one line means one record.

To get the number directly:


dig TXT example.com +short | grep -c "^v=spf1"

On Windows:


nslookup -type=txt example.com

Count the entries that start with v=spf1. dig joins the strings of one record into a single line, so a record the DNS panel wraps across three lines still counts once. If the count is two or more, you have the problem.

Run it against every name you send from, not only the apex domain. If you have delegated sending to mail.example.com or news.example.com, those names carry their own records and their own duplicates.

Check 2: read the header of a real message

Send yourself a test message, open "Show original" in Gmail or Outlook, and read the Received-SPF header.

A permerror there means the receiver hit the same problem you do. A pass there does not prove your record set is clean. It only tells you what that one receiver did with it, on that one message. A domain can pass at one receiver and return permerror at another on the same day.

Check 3: look for permerror in DMARC aggregate reports

Aggregate reports list the result per source. permerror in the SPF column for sources that used to pass is the signal that something changed in the zone.

DMARCFlow's DNS Health view shows SPF, DKIM, DMARC and BIMI compliance per domain, with an issue count and a last checked timestamp, so a duplicate shows up as a standing issue instead of a surprise. For a single domain the SPF Checker is the faster first pass and gives you the same finding in one lookup. If you would rather not read raw XML at all, start there.

How to fix it: merge the records into one

Merge the includes, keep the provider syntax

Before:


example.com. TXT "v=spf1 include:_spf.google.com ~all"
example.com. TXT "v=spf1 include:spf.eu.mailgun.org ~all"

After:


example.com. TXT "v=spf1 include:_spf.google.com include:spf.eu.mailgun.org ~all"

Rules for the merged record:

  • One version tag, one ending. -all for a hard fail, ~all for a soft fail. Never +all, which authorizes everyone.
  • Use the include string each provider publishes in its own documentation, not one copied from a forum answer. Include syntax differs per provider, and a wrong include silently authorizes nobody.
  • Keep the record you trust as the base and add the rest to it. The order does not matter. The count does.
  • Delete the duplicate in the same change, not later. A zone that holds both records stays broken even after you publish the merged one.

Stay under the 10 DNS lookup limit

Every include:, a, mx, exists: and redirect= costs a DNS lookup during evaluation, and the terms inside the included records count too. RFC 7208, Section 4.6.4: "If this limit is exceeded, the implementation MUST return 'permerror'."

Merging is not permission to paste every include you were ever handed. Three ways out when the merged record approaches the limit:

  • Flatten. Replace an include with the IP ranges it resolves to, and keep them updated.
  • Redirect. redirect= points evaluation at one other domain, which keeps the record short at the cost of a second name to maintain.
  • Delegate. Point a subdomain at one provider and give that subdomain its own SPF and DKIM, so each record stays small.

Delete the duplicate and verify

After the change, re-run the count. Then wait for the old record's TTL to expire: usually minutes, longer if the zone carries a high TTL.

Then send a real message to a mailbox where you can read the headers, and confirm the SPF result is a pass and not a permerror. Fixing the zone and checking the zone are two different steps, and the second one is the one people skip.

How to stop it coming back

Put DNS changes under change control

The duplicate is almost always added by someone who was not told they were touching email authentication. A marketing colleague connecting a newsletter tool is doing their job well.

What works:

  • One place where TXT records get changed, with a ticket attached.
  • A written rule: when a provider says "add this SPF record", the action is to merge the include into the existing record, never to add a new one.
  • A check after every onboarding, not only when deliverability drops.

Check the domain on a schedule, not on the day it breaks

Run the command on a schedule, or let a tool do it. In DMARCFlow, the DNS Health view covers every domain in the account, so a duplicate SPF record on any of them is flagged on the day it appears instead of when a customer writes in, and the last checked timestamp tells you whether you are looking at a current problem or a stale one.

For a small domain portfolio the command is enough. Run it monthly and after every change.

FAQ

Can I have two SPF records for two different email services?
No. Both services belong inside one record, as two include statements or their IP ranges. Two records is an RFC 7208 violation and the check result is permerror.

Does a second SPF record break my DMARC record?
No. The DMARC record at _dmarc.example.com is a separate DNS record and is unaffected. What you lose is SPF support. If DKIM is aligned, DMARC still passes on the strength of DKIM alone.

My domain has two SPF records and SPF still passes. Is it fine?
No. The spec result is permerror. A receiver that evaluates the first record anyway is tolerating an invalid configuration, and you have no SPF fallback for the day DKIM does not cover a message.

Should I use redirect= instead of merging the includes?
Use redirect= when you are near the 10 lookup limit and flattening is not practical. It keeps one record short. It is not a way to keep two records.

How long does the fix take to propagate?
It depends on the TTL of the TXT records. Usually minutes, occasionally up to 48 hours in the worst case. Re-run the count until it returns one record, then verify with a real message after the old record has expired.

Does this apply to subdomains too?
Yes, per DNS name. A subdomain can hold its own SPF record and that is not a conflict with the apex domain. It is a clean way to keep sending platforms apart: one subdomain, one record.

How do I merge two SPF records without exceeding the 10 DNS lookup limit?
Count the lookups in the merged record, including the terms inside each included record. Near the limit, flatten an include to IP ranges, use redirect=, or delegate a subdomain with its own record.

What is the difference between two SPF records and one SPF record split into two strings?
One record carried in several strings is a single TXT resource record whose value sits in several character-strings. DNS concatenates them and the record is legal. Two separate TXT records that both start with v=spf1 are two records, and that is what breaks SPF.