Back to blog
Threat Intel
Phishing Forensics

The AWS SES SPF Playbook: Taming PermErrors with Custom MAIL FROMs

Your default AWS SES SPF configuration is a ticking time bomb for PermErrors; here's the field guide to fixing it for good.

MailSleuth Research
Email Security Team
July 18, 20268 min read
Illustration of a tall, unstable tower of blocks, with one glowing block being moved to a separate, stable foundation.

You’ve seen the DMARC aggregate report. It’s a sea of red, littered with failures from your own infrastructure. Specifically, your application's transactional emails sent via AWS SES are getting tagged with SPF 'permerror'. Your deliverability is tanking, users aren't getting password resets, and the alerts are piling up. You checked your SPF record. It looks right. What gives?

The culprit is almost always the same: a well-intentioned but ultimately fragile configuration centered on the default `include:amazonses.com`. This approach works fine in a vacuum. But your mail ecosystem isn't a vacuum. It's a crowded, messy reality of multiple vendors, each demanding a piece of your DNS.

The path to a resilient, scalable AWS SES SPF configuration runs directly through a custom MAIL FROM domain. It’s not just a nice-to-have for branding; it’s a structural necessity for anyone managing more than a single email sender. This is the playbook for getting it right.

That `include:amazonses.com` in Your SPF Record Is a Landmine

When you first set up AWS SES, the documentation guides you to add `include:amazonses.com` to your domain's SPF record. It seems straightforward. You're telling the world, 'I authorize Amazon to send email on my behalf.' You add it, the verification light turns green, and you move on. This is a mistake.

The 10-Lookup Death Spiral

RFC 7208, which defines SPF, specifies a hard limit of 10 DNS lookups per SPF check. This includes any `include`, `a`, `mx`, `ptr`, or `exists` mechanisms. A receiving mail transfer agent (MTA) doesn't just see `include:amazonses.com`; it recursively resolves it. And therein lies the trap. That single `include` is not a single lookup.

When a receiver checks `amazonses.com`, it finds another SPF record: `"v=spf1 include:spf1.amazonses.com include:spf2.amazonses.com -all"`. So your one line in the SPF record has just cost you three lookups (the initial `include`, plus the two nested ones). Now add Microsoft 365 (`include:spf.protection.outlook.com`, another 2 lookups) and Salesforce (`include:_spf.salesforce.com`, can be 2-3 lookups). You're already at 7 or 8 lookups before you've even accounted for your own `mx` record. Add one more marketing tool and you're over the limit.

When a verifier exceeds the 10-lookup limit, it MUST return a "PermError" result.

Exceeding the limit isn't a soft warning. It's a hard failure. A PermError (Permanent Error) tells the receiver that your SPF record is broken and cannot be evaluated. For many mail servers, a PermError is treated the same as a `fail`. Your legitimate email, sent from an authorized source, ends up in spam or rejected outright because of a chain-reaction DNS problem.

How a Custom MAIL FROM Rewrites the SPF Game

To fix the lookup problem, you need to understand what SPF actually checks. It doesn't validate the friendly `From:` address a user sees in their email client. It validates the *envelope* sender, specified in the SMTP `MAIL FROM` command. This is often called the Return-Path, Bounce Address, or RFC5321.From address. By default, SES uses an envelope sender like `0102018a123b4cde-…-000000@us-east-1.amazonses.com`.

Shifting the Burden of the SPF Check

This is where a custom MAIL FROM domain becomes your most powerful tool. When you configure SES to use a custom MAIL FROM domain—say, `bounces.yourcorp.com`—you change the envelope sender for every email you send through SES. The `MAIL FROM` now becomes `something@bounces.yourcorp.com`.

The crucial shift is this: the receiving server no longer checks the SPF record of `yourcorp.com`. It checks the SPF record of `bounces.yourcorp.com`. This allows you to completely remove `include:amazonses.com` from your top-level domain's crowded SPF record. Instead, you create a dedicated, pristine SPF record for the subdomain `bounces.yourcorp.com` that contains only one thing: `v=spf1 include:amazonses.com -all`. You’ve effectively offloaded the AWS SES check to a subdomain, freeing up lookup slots on your primary domain.

The Double-Win: DMARC Alignment

There's a second, equally important benefit: DMARC alignment. According to RFC 7489, for DMARC to pass with SPF, the domain in the `MAIL FROM` address must align with the domain in the `From:` header. When you use the default SES MAIL FROM (`...@amazonses.com`), it can never align with your `From:` header (`...@yourcorp.com`). This results in an SPF 'pass' but a DMARC 'fail' for alignment.

By using `bounces.yourcorp.com`, a subdomain of `yourcorp.com`, you achieve what DMARC calls "relaxed alignment." The `MAIL FROM` domain and the `From:` header domain now share the same organizational root. This allows DMARC to pass, even if DKIM were to fail for some reason (like a mailing list forwarder modifying the message body). A custom MAIL FROM makes your email authentication far more resilient.

Not All `amazonses.com` Includes Are Created Equal

So you've moved your SES configuration to a custom MAIL FROM subdomain. Fantastic. Its SPF record now has `v=spf1 include:amazonses.com -all`. We can do better. That global `include` is inefficient, and we're hunting for every optimization we can find.

The `amazonses.com` SPF record is designed to authorize sending from *any* AWS region. But are you actually sending from every region? Probably not. If your application and your SES sending identity are configured in `us-west-2`, there's no reason to have an SPF record that also includes IPs for `eu-central-1`.

v=spf1 include:spf.us-east-1.amazonses.com -all

Instead of the global include, you can use a region-specific one. For example, if you send from US East (N. Virginia), your subdomain's SPF record should be `v=spf1 include:spf.us-east-1.amazonses.com -all`. This change has a material impact on DNS lookups. The global `include:amazonses.com` resolves to two more includes (`spf1.amazonses.com` and `spf2.amazonses.com`), costing 3 total DNS lookups. The regional record, like `spf.us-east-1.amazonses.com`, contains the IP addresses directly. It costs only 1 DNS lookup.

You just saved two lookups. That might not sound like much, but two lookups is an entire other service you can now add to your primary domain's SPF record without fear. This is the difference between a system that works and a system that is one acquisition away from failing. Pin your includes to the specific regions you use. If you genuinely use multiple regions, include only the specific regional records you need.

Playbook: Weaving SES into a Crowded SPF Record

Let's make this concrete. Imagine you're the admin for `yourcorp.com`. You use Microsoft 365 for employee email, Salesforce for your sales team, and AWS SES to send transactional password resets from your app, which is hosted in `eu-west-1`.

The 'Before' State: A Ticking Clock

Your initial setup at `yourcorp.com` likely looks like this: `v=spf1 include:spf.protection.outlook.com include:_spf.salesforce.com include:amazonses.com -all`. Let's do the math: M365 (2 lookups), Salesforce (2), global SES (3). That's 7 DNS lookups. Marketing now wants to add a new analytics tool that needs another 3-lookup include. You can't do it. You're at capacity.

The 'After' State: Strategic Delegation

First, we create a custom MAIL FROM domain in SES for `mail.yourcorp.com`. This involves creating an MX record and a TXT record for `mail.yourcorp.com` as instructed by the AWS console.

Next, we rewrite our DNS records. The key is separation of concerns.

yourcorp.com. TXT "v=spf1 include:spf.protection.outlook.com include:_spf.salesforce.com -all"
mail.yourcorp.com. TXT "v=spf1 include:spf.eu-west-1.amazonses.com -all" — DNS records after refactoring

Look at the difference. The primary domain, `yourcorp.com`, now has an SPF record with a lookup count of just 4. We've removed AWS SES from it entirely. All SES mail now uses the envelope sender `mail.yourcorp.com`, passes SPF via its own dedicated and hyper-efficient regional record (1 lookup), and achieves DMARC alignment. You now have 6 lookup slots available on your main domain. Marketing can have their new tool. You can sleep at night.

Verification Is Not Optional: Trust, but Check Your DNS

An SPF record is an instruction set for the entire world's mail servers. It's code. You wouldn't deploy application code without running tests, and you shouldn't publish DNS records without end-to-end verification. A typo in an SPF record can take your entire email program offline.

Propagation is the first step. After making a change, use a tool like `dig` to query your TXT record from multiple vantage points. `dig TXT mail.yourcorp.com`. But don't stop there. Seeing the record exists is not the same as validating its logic.

Simulating a Real-World Check

You must use a tool that simulates how a receiving MTA processes an SPF record. A good SPF checker will recursively resolve every `include` and `redirect`, follow `a` and `mx` lookups, and present you with a final, authoritative count of DNS queries.

The report from such a tool is your pre-flight checklist. It must show a lookup count well below 10. It must not have any syntax errors. It must not have any void lookups (lookups to domains that don't exist). This check is not a one-and-done activity. Your vendors—Microsoft, Salesforce, AWS—can and do change their own SPF records. An `include` that costs 2 lookups today might cost 3 tomorrow without any warning. This makes periodic auditing of your SPF records a non-negotiable part of domain hygiene.

The takeaway

SPF isn't a checkbox to tick for compliance; it's a resource to be managed. Every `include` is a dependency, a point of potential failure. By treating your SPF record with the same discipline you apply to your codebase—by isolating dependencies, using subdomains strategically, and pinning to specifics like AWS regions—you move from a reactive to a proactive security posture.

The final arbiter of success is your DMARC data. Those aggregate reports are the ground truth of how receivers worldwide see your mail. They will show you precisely which sources are passing, which are failing, and whether your alignment strategies are working. Use an analyzer to make sense of the raw XML reports, as it's the fastest way to turn that data into an actionable remediation plan. Don't just aim for a 'pass' on a checker; aim for flawless delivery reflected in your DMARC reports. That's how you know you've gotten it right.

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

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