Back to blog
Threat Intel
Phishing Forensics

When Salesforce Shatters Your SPF: The 10-Lookup Limit Trap

That generic Salesforce SPF `include` is a ticking time bomb for your DMARC compliance, and here’s the exact playbook to defuse it.

MailSleuth Research
Email Security Team
June 19, 20267 min read
An illustration of the SPF 10-lookup limit showing a chain of dominoes, started by Salesforce, about to fall off a cliff

You just onboarded Salesforce Marketing Cloud. You followed the setup guide, dutifully adding `include:_spf.salesforce.com` to your domain's TXT record, and moved on. A week later, your DMARC aggregate reports start lighting up with authentication failures from IPs you know belong to Salesforce. But they aren't explicit fails—they're `PermError` results. Your SPF record hasn't just broken; it's invalid.

This isn't a bug. It's a feature of how SPF and large service providers interact, a collision between a decade-old specification and the realities of cloud infrastructure. That single line you added is a gateway to a cascade of DNS lookups, and you've just fallen victim to SPF's hard limit of 10.

This scenario is one of the most common—and entirely fixable—causes of DMARC misalignment for organizations using Salesforce. Fixing it requires understanding not just what broke, but why the specific nature of this breakage undermines your entire email security posture.

Anatomy of a `PermError`: Deconstructing salesforce.com's SPF

The problem starts with a fundamental misunderstanding of what an SPF `include` mechanism actually does. It's not a reference; it's a recursive instruction. RFC 7208, which defines the Sender Policy Framework, states that the total number of mechanisms and modifiers that cause DNS lookups MUST NOT exceed 10 per SPF check. This includes any lookups caused by `include` mechanisms or the `mx` mechanism.

When a receiving mail server evaluates your SPF record and encounters `include:_spf.salesforce.com`, it performs a DNS TXT query for `_spf.salesforce.com`. This counts as one lookup. But the story doesn't end there.

The Nested Doll of DNS Lookups

The record at `_spf.salesforce.com` is not a simple list of IP addresses. It's a collection of more `include` mechanisms, pointing to other SPF records managed by Salesforce for their various sending infrastructures. At the time of writing, it looks something like this:

v=spf1 include:_spf1.salesforce.com include:_spf2.salesforce.com include:_spf3.salesforce.com include:_spf4.salesforce.com ~all

Suddenly, your one lookup has become five. The receiver must now query for `_spf1.salesforce.com`, `_spf2.salesforce.com`, and so on. Each of *those* records can contain further includes or IP ranges. Before you know it, the chain of lookups from this single Salesforce entry can easily consume 6, 7, or even 8 of your allotted 10 lookups. If your own SPF record already includes entries for Google Workspace, Microsoft 365, and another marketing tool, you've almost certainly blown past the limit.

Once a receiver's DNS resolver count hits 11 during an SPF evaluation, it stops immediately and returns a `PermError` (Permanent Error). The check is aborted. It’s not a `Pass`, and crucially, it's not a `Fail`. It’s a state of validation limbo.

Tracing the Failure with `dig`

Don't trust, verify. You can manually trace this entire chain yourself from any terminal with the `dig` command. This is how you prove to your team (and yourself) exactly what is happening under the hood.

Start with your own domain. Let's assume your domain is `example.com` and your SPF record contains includes for Microsoft 365 and Salesforce.

$ dig txt example.com +short
"v=spf1 include:spf.protection.outlook.com include:_spf.salesforce.com -all"

So far, you have two includes. Let's follow the Salesforce branch. Each `include` is one lookup.

Following the Chain

First, look up the Salesforce entry itself (Lookup #1 for this branch):

`$ dig txt _spf.salesforce.com +short`
You'll get back the list of nested includes we saw earlier. Let's just follow the first one in that list (Lookup #2):

`$ dig txt _spf1.salesforce.com +short`
This might return yet another record with more includes, like `include:_prod.salesforce.com`. So you continue (Lookup #3):

`$ dig txt _prod.salesforce.com +short`
This process continues until you finally resolve to a record containing only `ip4` or `ip6` mechanisms. By counting each `dig` command you run to follow the `include` path, you are simulating the resolver's process. It becomes painfully obvious how quickly you can exceed 10 lookups. If your base record had 4 includes, and the Salesforce one added 7 more on its own, your total is 11. The check fails.

Why `PermError` Is a DMARC Black Hole

An SPF `Fail` is a clear, actionable signal. It tells the receiver, "This IP is not authorized to send email for this domain." DMARC, as defined in RFC 7489, uses that signal. If SPF fails and is not aligned, and DKIM also fails or is not aligned, DMARC instructs the receiver to apply your `p=quarantine` or `p=reject` policy.

But a `PermError` is not a `Fail`. It's an error condition that means the validity of the sending IP could not be determined. Most mail servers, when faced with an SPF `PermError`, will treat it as if SPF was not present at all. They'll often return a result of `None` or `Neutral`.

This effectively neuters your DMARC policy for that message. The email from Salesforce, despite originating from a legitimate source, will fail SPF validation in a way that prevents DMARC from acting on it. If for some reason DKIM also fails (perhaps due to a mailing list forwarder modifying the body), that email now has no authentication signals. It might be delivered to the inbox with security warnings, or worse, get flagged as suspicious, hurting deliverability.

The operational stake is high: You have a security policy (`p=reject`) that you believe is protecting you from impersonation, but a misconfiguration is creating a blind spot where that policy doesn't apply. — A frustrated SOC Analyst

This is how legitimate, business-critical emails from your CRM or marketing platform end up in spam or get rejected, all because of an obscure DNS lookup limit. You aren't failing closed; you're failing open.

Remediation Playbook: The Custom SPF Fix

The fix is to stop using the generic, one-size-fits-all Salesforce include and replace it with one tailored specifically to your Salesforce instance. Salesforce doesn't use its entire IP space to send your email; it uses a specific subset, or 'instance'. By pointing your SPF record directly to the SPF record for your instance, you bypass the entire chain of nested lookups.

Step 1: Find Your Salesforce Instance ID

The most reliable way to find this information is to inspect the headers of an email sent from your Salesforce organization. Send a test email from Salesforce to a mailbox you control, and view the original message source to see all the headers. Look for `Received` headers. You are looking for a server name ending in `salesforce.com`. It will often look something like `[some-id].bn1.bn.salesforce.com` or `[some-id].iad1.mail.salesforce.com`.

The key part is the instance identifier, which often corresponds to a datacenter location (e.g., `na15`, `eu27`, `ap12`). Your company's Salesforce administrator may also know this information. It's the same identifier you see in your browser's URL bar when logged into your Salesforce environment.

Step 2: Construct the Instance-Specific Include

Once you have your instance ID, you can construct the correct `include` statement. The pattern is typically `include:_spf.<instance>.salesforce.com`. For example, if your instance is `na112`, your new include would be `include:_spf.na112.salesforce.com`.

Replace the bloated `include:_spf.salesforce.com` in your DNS record with this new, specific one. Your `v=spf1...` record goes from referencing a record with 4+ includes to one that might have zero nested includes and just a list of IPs. You've just reduced your lookup count for Salesforce from 5+ down to 1.

Verify the Fix and Monitor

After you've updated your DNS record and waited for the TTL to expire, you must verify the fix. Hope is not a strategy. Use an online SPF record checker. There are many free tools available that will take your domain name, walk the entire lookup chain, and present you with a final count.

Enter your domain, and the tool should now report a total lookup count well under 10. You should see it trace your new, specific Salesforce include and stop, contributing only one lookup to the total. The tool should no longer flag a `PermError` or a "too many lookups" warning.

The second part of verification is monitoring. Keep a close eye on your DMARC aggregate reports for the next few days. The percentage of messages from Salesforce IPs passing DMARC should trend toward 100%. The `PermError` results for SPF should disappear, replaced by clean `Pass` results. This is your confirmation that receiving mail servers are now correctly validating your Salesforce emails against your security policy.

The takeaway

The Salesforce SPF lookup issue isn't an isolated problem. It’s a classic example of technical debt in internet standards. SPF was designed in an era before massive, multi-tenant cloud platforms were the norm. While a future standard like authenticated received chain (ARC, RFC 8617) helps with forwarding, the core SPF limitations remain.

The key takeaway is to treat any third-party service's generic SPF include with suspicion. Always trace the lookups. If a vendor provides a generic entry that expands into a half-dozen more, push back and ask for an instance-specific record. It's your domain's reputation on the line. Tools that continuously monitor DMARC and underlying authentication, like MailSleuth.AI, can surface these kinds of configuration drifts before they become deliverability incidents.

#spf#dmarc#email-security#salesforce#dns
MailSleuth Research
Email Security Team

We dissect phishing campaigns and email infrastructure so you don't have to.