Aligning SPF, DKIM and DMARC for Third-Party Senders
After working through this guide you will be able to take a domain that six different SaaS products send mail from, get every one of them into DMARC alignment, keep the SPF record inside the ten-lookup budget, and ramp to p=reject using evidence from aggregate reports rather than hope.
The failure this guide addresses is specific and extremely common: your marketing platform’s mail passes SPF, passes DKIM, and still fails DMARC. Nothing is broken in the vendor’s configuration. The check that fails is alignment — the requirement that the domain the recipient authenticated is related to the domain the human sees in the From line. Vendors sign with their own domain and bounce to their own domain by default, and neither has anything to do with yours. Fixing that is a per-vendor job, and doing it wrong at scale is how a domain ends up with fourteen include: mechanisms and a permanent SPF permerror.
Key objectives:
- Read alignment as a separate check from SPF and DKIM authentication, and know when
rversussmatters. - Give each third-party sender its own subdomain, custom return-path, and CNAME-delegated DKIM selector.
- Keep the apex SPF record under ten DNS-resolving mechanisms without resorting to fragile flattening.
- Drive the move from
p=nonetop=rejectoff aggregate report data, one vendor at a time.
Why a passing SPF check still fails DMARC
SPF authenticates the envelope sender — the MAIL FROM address in the SMTP transaction — and nothing else. When a ticketing platform sends on your behalf, its envelope sender is usually something like [email protected]. The receiving server looks up SPF for mailer.vendor.io, finds the vendor’s own record, sees the vendor’s own sending IP, and returns pass. That result is genuine and completely useless to DMARC, because the domain that passed is not related to example.com, which is what the recipient sees in the From line.
DMARC adds the missing step. It extracts the From header domain and asks whether either authenticated identifier lines up with it:
- SPF alignment compares the From domain with the envelope
MAIL FROMdomain. - DKIM alignment compares the From domain with the
d=value of a signature that verified.
Either one passing is enough. Both failing means DMARC fails, regardless of how many green checkmarks the individual mechanisms produced. The comparison mode is set by two tags on your _dmarc record: aspf and adkim, each r (relaxed, the default) or s (strict).
Relaxed mode compares organizational domains, so mail.example.com aligns with example.com and any subdomain works. Strict mode requires an exact FQDN match, which means a vendor signing as mail.example.com fails against a From of [email protected]. Start relaxed. Strict is worth it only when you have deliberately locked every sender to an exact name and want to block a compromised subdomain from authenticating as the apex.
| Scenario | SPF result | SPF aligned | DKIM result | DKIM aligned | DMARC |
|---|---|---|---|---|---|
| Vendor default, no configuration | pass (vendor domain) | no | pass (d=vendor.io) |
no | fail |
| Custom return-path only | pass | yes | pass (d=vendor.io) |
no | pass |
| Vendor DKIM CNAME only | pass (vendor domain) | no | pass (d=news.example.com) |
yes | pass |
| Both configured | pass | yes | pass | yes | pass |
| Forwarded message, both configured | fail (forwarder IP) | n/a | pass | yes | pass |
The last row is the argument for never relying on SPF alone. A mailing list or a corporate forwarder rewrites the envelope and breaks SPF every time; a DKIM signature over the message body survives, provided the forwarder does not modify it. Aligned DKIM is what keeps legitimate forwarded mail deliverable under p=reject.
Step 1 — Inventory every sender before touching a record
You cannot align senders you do not know about. Build the list from three sources: your billing records, the Received headers of mail people have actually received, and after publishing p=none, the aggregate reports themselves. Expect to find at least one sender nobody remembered.
| Vendor | Purpose | From domain to use | Return-path | DKIM method |
|---|---|---|---|---|
| Google Workspace | Human mail | example.com |
apex | selector TXT |
| Marketing SaaS | Campaigns | news.example.com |
em4211.news.example.com |
CNAME selectors |
| Ticketing SaaS | Support replies | help.example.com |
bounce.help.example.com |
CNAME selectors |
| Billing SaaS | Invoices | billing.example.com |
bounce.billing.example.com |
CNAME selector |
| CI / alerting | Internal alerts | alerts.example.com |
vendor default | selector TXT |
| Legacy CRM | Being retired | none | none | none |
Record for each one whether it supports a custom return-path domain, whether it offers CNAME-delegated DKIM, and how many DNS lookups its include: costs. Those three facts determine everything that follows.
Step 2 — Give each vendor its own subdomain
The single highest-leverage decision is to stop sending everything as the apex. A per-vendor subdomain gives each sender its own SPF record, its own DKIM namespace, and — critically — its own reputation. A campaign that trips a spam filter damages news.example.com, not the domain your invoices and password resets go out from.
Each subdomain carries a complete, self-contained set of records. Note that the apex SPF record does not need to mention the vendor at all once mail is sent as the subdomain:
news.example.com. 3600 IN TXT "v=spf1 include:sendgrid.net -all"
news.example.com. 3600 IN MX 0 .
_dmarc.news.example.com. 3600 IN TXT "v=DMARC1; p=none; rua=mailto:[email protected]"
example.com. 3600 IN TXT "v=spf1 include:_spf.google.com -all"
The Null MX on news.example.com matters: the subdomain sends but never receives, and without an explicit 0 . an MTA falls back to the implicit MX and tries to deliver mail to whatever address record it finds. Publish subdomain records as part of the same change set as the vendor onboarding, ideally through code — see Managing DNS Zones as Code with Terraform for the workflow that keeps a dozen vendor records reviewable.
Step 3 — Point the return-path at your own domain
A custom return-path (also sold as a “custom bounce domain” or “custom MAIL FROM”) is what buys SPF alignment. The vendor gives you a hostname to publish under your zone; bounces then flow to a name that is organizationally yours, so the SPF check runs against your record and aligns with your From domain.
Two shapes are common. Some vendors hand you a CNAME:
em4211.news.example.com. 3600 IN CNAME u1234567.wl.sendgrid.net.
Others require you to own the record set outright, which means an SPF record and an MX so bounces can be delivered:
bounce.help.example.com. 3600 IN TXT "v=spf1 include:mailgun.org ~all"
bounce.help.example.com. 3600 IN MX 10 mxa.mailgun.org.
bounce.help.example.com. 3600 IN MX 10 mxb.mailgun.org.
Either way the effect is the same: MAIL FROM becomes [email protected], which shares an organizational domain with [email protected], and relaxed SPF alignment passes. Under strict alignment it would not — another reason to keep aspf=r.
Step 4 — Delegate DKIM selectors with CNAMEs
Never paste a vendor’s public key into a TXT record if the vendor offers CNAME delegation. A CNAME lets the vendor rotate its key without asking you to edit DNS, which removes the most common cause of a DKIM failure eighteen months after onboarding.
s1._domainkey.news.example.com. 3600 IN CNAME s1.domainkey.u1234567.wl.sendgrid.net.
s2._domainkey.news.example.com. 3600 IN CNAME s2.domainkey.u1234567.wl.sendgrid.net.
k1._domainkey.help.example.com. 3600 IN CNAME dkim.mcsv.net.
The vendor now signs with d=news.example.com, which aligns. Two selectors are normal — vendors publish a pair so they can rotate between them. If a vendor only supports a TXT key, generate it with the longest key length they allow and diarize the rotation; the mechanics are in Setting Up DKIM Signing for Your Domain.
Step 5 — Stay inside the ten-lookup budget
SPF permits ten mechanisms that trigger DNS resolution — include, a, mx, ptr, exists, and redirect. Exceed it and evaluation returns permerror, which most receivers treat as an SPF failure, taking your alignment with it. The count is recursive: a vendor’s include: that itself contains three include: mechanisms costs you four, not one.
dig +short TXT _spf.google.com
dig +short TXT sendgrid.net | tr ' ' '\n' | grep -c include
The subdomain strategy in Step 2 is also the lookup-limit strategy: each subdomain gets its own record with its own budget of ten, and the apex only needs the sender that actually sends as the apex. That is the durable fix.
SPF flattening — expanding every include: into literal ip4: and ip6: ranges — is the tempting shortcut, and it is genuinely risky. The vendor’s IP ranges are theirs to change without telling you, and a flattened record goes stale silently: mail keeps flowing until the vendor adds a new range, at which point a fraction of your mail starts failing SPF with no configuration change on your side. If you flatten anyway, automate the regeneration on a schedule, diff against the vendor’s live record, alert on any change, and never flatten a vendor whose ranges you have seen move. The full mechanics and the permerror diagnosis are covered in Configuring SPF Records to Prevent Spoofing.
Step 6 — Ramp to reject on evidence
Publish the monitoring policy first, with a reporting address you actually read:
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=none; adkim=r; aspf=r; rua=mailto:[email protected]; fo=1"
Leave it for two to four weeks so weekly and monthly senders appear. Then work the vendor list from Step 1, publishing return-path and selector records one vendor at a time so a regression is attributable. When aggregate reports show every legitimate source aligned for a full reporting cycle, move to p=quarantine; pct=25, then pct=100, then p=reject. Keep rua in place forever — it is the only ongoing visibility you have. The broader policy rollout, including sp and ruf, is covered in Deploying DMARC with Monitoring and Enforcement.
Verification
Confirm the records exist before you test mail flow:
dig +short TXT news.example.com
dig +short CNAME s1._domainkey.news.example.com
dig +short TXT _dmarc.example.com
"v=spf1 include:sendgrid.net -all"
s1.domainkey.u1234567.wl.sendgrid.net.
"v=DMARC1; p=none; adkim=r; aspf=r; rua=mailto:[email protected]; fo=1"
Then send a real message through the vendor and inspect what the receiver saw. If you have SMTP credentials, swaks lets you set the envelope and header senders independently, which is exactly the pair alignment depends on:
swaks --server smtp.sendgrid.net:587 --auth LOGIN \
--from [email protected] \
--h-From 'News <[email protected]>' \
--to [email protected] \
--header 'Subject: alignment test'
Read the Authentication-Results header on the delivered message. This is the line that settles the argument:
Authentication-Results: mx.receiver.test;
dkim=pass [email protected] header.s=s1;
spf=pass smtp.mailfrom=em4211.news.example.com;
dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=example.com
header.from is the domain being protected; smtp.mailfrom and header.i are the identifiers being aligned against it. If dmarc=fail appears while both dkim=pass and spf=pass are present, alignment is the problem and the domains in those three fields will show you which one is wrong.
Reading an aggregate report to spot an unaligned sender
Aggregate reports are gzipped XML sent daily. Each <record> groups messages by sending IP and gives you both the raw result and the aligned result — the pair that matters:
gunzip -c receiver.test\!example.com\!1753660800\!1753747200.xml.gz | xmllint --format -
198.51.100.77
412
none
fail
fail
example.com
vendor.io pass
mailer.vendor.io pass
Read it inside out. auth_results shows both mechanisms passing — the vendor is configured correctly and is not a spoofer. policy_evaluated shows both failing, because the domains in auth_results do not match header_from. Four hundred and twelve messages a day from a vendor you recognize, passing authentication and failing alignment, is the signature of a sender that needs Steps 3 and 4 applied. Contrast that with a genuine spoofer, which shows pass nowhere and usually comes from a scattering of single-message source IPs.
Troubleshooting
DKIM passes but DMARC fails. The signature verified against the vendor’s domain, not yours. Check header.i or the d= tag in the DKIM-Signature header: if it reads vendor.io, the CNAME selectors from Step 4 were never published or the vendor was never switched to signing with your domain in its dashboard. Publishing the CNAME is only half the job — most vendors need the domain “verified” in their UI before they start signing with it.
SPF returns permerror after adding one vendor. You crossed ten lookups. Count them with dig against each include: target, remembering the count is recursive. The fix is to move that vendor to its own subdomain rather than to flatten, because flattening trades a visible error today for an invisible one later.
Alignment works in test but fails for a subset of production mail. The vendor is sending from more than one system — transactional and marketing streams often use different infrastructure and different envelope domains under one account. Check whether the aggregate reports show two distinct source IP ranges under the same vendor, and configure the return-path for both streams.
Mail from a subdomain is rejected once the apex reaches p=reject. Subdomains inherit the apex policy unless sp says otherwise, and a subdomain with no records of its own inherits nothing useful for authentication. Publish an SPF record and either a DKIM selector or a return-path for every subdomain that sends, or set sp=none temporarily while you work through them.
A forwarded newsletter fails at a mailbox provider. SPF broke at the forwarder, which is expected, so DMARC now depends entirely on DKIM surviving. If the forwarder appends a footer or rewrites headers the signature breaks. Sign with relaxed canonicalization (c=relaxed/relaxed), keep the signed header list short, and confirm the vendor is not signing headers that intermediaries commonly rewrite.
Frequently Asked Questions
Why does my vendor’s mail fail DMARC when both SPF and DKIM pass? Because DMARC checks alignment, not just authentication. The vendor authenticated its own domain in both mechanisms, and neither matches the domain in your From header. Publish a custom return-path under your domain, delegate DKIM selectors with CNAMEs, or both — either one produces an aligned identifier and DMARC passes.
Should I use relaxed or strict alignment?
Relaxed, in almost every case. Strict alignment rejects a vendor signing as mail.example.com when the From header says example.com, which is precisely the subdomain arrangement that makes third-party senders manageable. Reserve strict for a domain with a single, tightly controlled sender.
Is SPF flattening safe? It works until the vendor changes its IP ranges, and then it fails silently for a portion of your mail. If you must flatten, regenerate on a schedule, diff against the vendor’s live record, and alert on any change. Splitting senders across subdomains solves the lookup limit without taking on that risk.
Do I need a separate DMARC record for each subdomain?
Not necessarily — subdomains inherit the apex policy, and sp lets you set a different one for all of them at once. A dedicated _dmarc record on a sending subdomain is still useful during rollout, because it lets you run that vendor at p=none while the apex is already at reject.
How long should I stay at p=none before enforcing? Long enough for every sender to appear at least twice in aggregate reports, which usually means two to four weeks and always includes a full monthly billing cycle. Move on when the reports show zero unaligned legitimate sources, not when a fixed number of days has passed.
Related
- Configuring SPF Records to Prevent Spoofing
- Setting Up DKIM Signing for Your Domain
- Deploying DMARC with Monitoring and Enforcement
- Managing DNS Zones as Code with Terraform
Back to Advanced SRV & MX Routing