Back to blog
Threat Intel
Phishing Forensics

SendGrid's PermError Trap: Why Your 'Valid' SPF Record Is Failing

Your SPF record includes `sendgrid.net`, but DMARC reports show a `permerror`—here's the technical reason why, and the right way to fix it.

MailSleuth Research
Email Security Team
July 31, 20266 min read
An illustration of a tall stack of blocks about to collapse as one final block is added, symbolizing an SPF record excee

You’re staring at a DMARC aggregate report. A big red block of failures jumps out, all from a familiar IP range: SendGrid. You check your DNS. The SPF record is right there, just like their documentation says: `include:sendgrid.net`. It validates in every online checker. Yet the reports don't lie. You're getting a steady stream of `spf=permerror` results.

This isn't a typo in your DNS or a bug at SendGrid. It's a fundamental conflict between how modern ESPs manage massive IP pools and a critical, often-overlooked constraint baked into the SPF protocol itself.

The problem isn't that `include:sendgrid.net` is wrong. The problem is that it's just one piece of a much larger, and more fragile, puzzle. Your SPF record is a ticking time bomb, and SendGrid might just be the service that finally sets it off.

The Anatomy of a 'Single' SendGrid Include

When a receiving mail server evaluates your SPF record, it doesn't just see the string `include:sendgrid.net`. It triggers a recursive DNS query to resolve what that mechanism actually authorizes. And with SendGrid, it's never just one query.

Following the Lookup Chain

Let's trace the path. A query for the TXT record at `sendgrid.net` doesn't return a list of IPs. Instead, it returns another `include`:

v=spf1 include:_spf.sendgrid.com -all

That's one DNS lookup. The server then has to query `_spf.sendgrid.com`. This, in turn, points to three more `include` statements, designed to cover their vast network infrastructure. So your single `include:sendgrid.net` has already burned four DNS lookups (1 for the initial include, 3 for the subsequent ones) just to resolve SendGrid's primary netblocks. This structure is a necessity for them; it allows their network team to add or remove IP ranges by only updating one of the downstream records, without forcing every customer to change their own DNS. But it comes at a cost to you.

The Real Culprit: RFC 7208's Ten Lookup Limit

The Sender Policy Framework, defined in RFC 7208, imposes a strict, non-negotiable limit: a receiving server must not perform more than ten DNS lookups while evaluating a single SPF check. This includes all `include`, `a`, `mx`, `ptr`, and `exists` mechanisms, and any nested lookups they trigger.

Why the limit? It's a defense mechanism. Without it, an attacker could craft a malicious SPF record with a circular chain of `include` statements, tricking a mail server into a DNS query loop and creating a denial-of-service vulnerability. The ten-lookup limit acts as a circuit breaker.

How You Hit the Wall

Most organizations don't send email from a single service. Your SPF record is likely a collage of providers. Consider a typical setup:

v=spf1 include:_spf.google.com include:servers.mcsv.net include:mail.zendesk.com include:sendgrid.net ~all

Let's do the math. Google Workspace's `_spf.google.com` consumes three lookups. Mailchimp's `servers.mcsv.net` takes two. Zendesk can take two or three. Add SendGrid's four lookups, and you're already at 11 or 12. You've exceeded the limit before the receiver even finishes parsing your record. When this happens, the receiver immediately stops processing and returns a `permerror`—a permanent error indicating a misconfiguration. To the receiver, your record is broken, even if every individual `include` is technically valid.

Isolating the Failure with DMARC Reports

This `permerror` condition is silent to you, the sender. Your emails still leave SendGrid. Some might even get delivered, as `permerror` often leads to a neutral or quarantine action rather than a hard reject. The only way to spot this systemic failure is by analyzing your DMARC aggregate (`rua`) reports.

Inside an XML report, you'll find records for specific IP addresses and their authentication results. You're looking for a pattern. Find a `<record>` block where the `<source_ip>` belongs to SendGrid. Then, look inside the `<auth_results>` tag for the SPF result. You'll see something like this:

<row>
<source_ip>168.245.10.230</source_ip>
<count>50</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<auth_results>
<spf>
<domain>yourdomain.com</domain>
<result>permerror</result>
</spf>
</auth_results>

The key is the `<result>permerror</result>` tag. The DMARC report flattens this to an SPF `fail` in the `<policy_evaluated>` section, which is what impacts your DMARC pass rate. When you see a known SendGrid IP alongside an SPF `permerror`, you've confirmed the 10-lookup limit is the root cause. Your DKIM might still be passing, but losing SPF alignment puts your deliverability at risk, especially with stricter DMARC policies (`p=quarantine` or `p=reject`).

The Solution: Isolate Services with Subdomain Delegation

The common impulse is to 'flatten' the SPF record—to resolve all the IP addresses from every `include` and paste them directly into your main SPF record. This is a catastrophic mistake. Third-party providers like SendGrid change their sending IPs constantly. A flattened record would be a maintenance nightmare, guaranteed to be outdated within weeks, if not days, causing legitimate mail to be rejected.

The Correct Pattern: One Subdomain Per Service

The only scalable, long-term solution is to stop treating your root domain's SPF record as a dumping ground for every service you use. Instead, you delegate authority for each service to a unique subdomain.

For SendGrid, the process looks like this:

1. Create a new subdomain for SendGrid traffic. A common convention is `sg.yourdomain.com`.
2. In your DNS, create a TXT record for this new subdomain: `sg.yourdomain.com TXT "v=spf1 include:sendgrid.net ~all"`.
3. Crucially, you must now configure SendGrid to send mail using this subdomain in the `Return-Path` (also known as the envelope from or MAIL FROM address). This is usually done by setting up a 'Branded Domain' or 'Authenticated Domain' within your SendGrid account settings.
4. Finally, you can remove `include:sendgrid.net` from your root domain's SPF record, freeing up those four lookups.

When a mail server receives an email from SendGrid, it now looks at the `Return-Path`, sees `bounces@sg.yourdomain.com`, and performs the SPF lookup against `sg.yourdomain.com`. The lookup counter for this check is fresh. The server evaluates `sg.yourdomain.com`'s SPF record, which only contains the SendGrid include and uses just four of its ten available lookups. The check passes cleanly.

Verifying the Fix and DMARC Alignment

After implementing subdomain delegation, verification is a two-step process. First, use any online SPF validator to check your new `sg.yourdomain.com` record. It should resolve correctly and show a lookup count of around four. More importantly, check your root domain's SPF record again. Its lookup count should now be significantly lower.

The second, more critical verification comes from DMARC. With subdomain delegation, your SPF authentication will now result in a `pass`. Furthermore, because the `Return-Path` domain (`sg.yourdomain.com`) shares a root domain with the `From:` header (`yourdomain.com`), it achieves SPF alignment under DMARC's default 'relaxed' setting. This is the goal: not just authentication, but aligned authentication.

Send a test campaign through SendGrid and inspect the headers of a received message. You should now see `spf=pass` in the `Authentication-Results` header. The ultimate proof will arrive in your DMARC reports 24-48 hours later, where the `permerror` count for SendGrid's IPs will have vanished, replaced by a healthy block of passing results.

The takeaway

The `permerror` from a crowded SPF record isn't a SendGrid problem; it's a protocol limitation you've hit. Piling more `include` mechanisms on your root domain is like building a house on a sinkhole. It will eventually collapse.

The disciplined approach is the only one that works: give every third-party sender its own subdomain. `mktg.yourdomain.com` for your marketing platform, `support.yourdomain.com` for your helpdesk, and `sg.yourdomain.com` for SendGrid. This keeps your root domain clean and immunizes you from the 10-lookup limit. It's not a quick hack; it's proper email infrastructure hygiene. Tools like MailSleuth.AI can help visualize this fragmentation in your DMARC data and confirm that your subdomain strategy is working as intended.

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

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