Intermediate
Lesson 8 of 11 · ~9 min

SPF in DNS

The two firm rules helpdesk techs trip over: only one SPF per domain, and a 10-DNS-lookup limit on evaluation. Both violations silently turn SPF off.

SPF is the email-authentication record helpdesk techs touch most. Every M365, Google Workspace, CRM, or newsletter-platform onboarding adds something to SPF. Every mail provider migration changes it.

The two rules that trip techs up are subtle: one SPF sounds like one TXT, but the TXT layer allows multiple records while SPF itself only allows one starting with v=spf1. The 10-lookup limit isn’t visible in the SPF string; you have to count it. Both rules produce permerror outcomes that look like mail-auth working fine until you look at a receiver’s DMARC report.

What an SPF record looks like

apex SPFtext
1example.com. IN TXT "v=spf1 include:spf.protection.outlook.com include:_spf.salesforce.com -all"

The mechanisms inside an SPF string are how it matches sending IPs: ip4:, ip4:.../24, a, mx, include:, exists:. In MSP work, include: is what you see most.

Rule 1: only one SPF per domain

Two SPF records = permerror = no SPF protection

The DNS TXT layer allows multiple TXTs on the same name. SPF adds a rule on top: only one TXT starting with v=spf1 per domain.

Two SPF records produce permerror — receivers treat the domain as if SPF protection isn’t in place. Add a CRM that wants “its SPF added”? Don’t create a second TXT. Merge into a single TXT.

Wrong:

example.com. IN TXT "v=spf1 include:spf.protection.outlook.com -all"
example.com. IN TXT "v=spf1 include:_spf.salesforce.com ~all"

Right:

example.com. IN TXT "v=spf1 include:spf.protection.outlook.com include:_spf.salesforce.com -all"

Rule 2: the 10-DNS-lookup limit

SPF specifies a limit of 10 DNS lookups during evaluation. Each include:, a, mx, ptr, exists mechanism counts as one. Includes count recursively (an include whose target uses 5 lookups counts 5 against your limit, not 1).

How to count, roughly:

Vendor includeLookups it uses
spf.protection.outlook.com (M365)~5
_spf.google.com (Google Workspace)~3
sendgrid.net (SendGrid)~4

You can quickly hit 10 by combining a few vendor includes. Over-limit = permerror = no SPF protection, even though the record is published.

The fix at the design level is SPF flattening: replacing include: mechanisms with explicit ip4: and ip6: ranges, which don’t count as lookups. Done manually it’s brittle (vendors change IPs); done with a managed SPF service it’s automatic. Either way, flattening is design territory and escalation, not helpdesk.

Practice: verify SPF before adding a sender

A client wants to add a new CRM that sends as their domain. Before you touch the record, check the current state.

spf-verify
Look up the current SPF record to see what's already published.
$ pick one

When to escalate

This lesson stops at identify-and-apply. The following are escalation:

  • SPF flattening when the lookup count exceeds 10.
  • Adding a new sender when the SPF is already at or near the limit.
  • DMARC enforcement showing SPF failures with no clear cause.
  • Conflicts between policy terms (~all vs -all) where the right choice depends on the client’s risk posture.

What to do with this

When asked to add a sender to a client’s SPF:

  1. Locate the existing TXT starting with v=spf1.
  2. Get the new include from the vendor’s setup docs.
  3. Merge into the existing TXT (don’t create a second one).
  4. Count lookups (MXToolbox’s SPF lookup count, EasyDMARC’s checker, or dig recursively).
  5. Publish and verify with dig TXT example.com.

If the lookup count would exceed 10, stop and escalate.

Decision walkthrough

SPF over the limit. Now what?
A client onboards a new marketing-email platform. The platform says: add include:_spf.example-marketing.example to your SPF. The current SPF already includes M365, Salesforce, and SendGrid (around 12 lookups total). The 10-lookup limit is already exceeded.
The current SPF is already at 12 lookups. The client wants a fourth sender. What do you do?
Loading quiz…
Next lesson