SPF for Microsoft 365 (Exchange Online): The Canonical Record, the spf.protection.outlook.com Include Chain, Third-Party Senders, Hybrid SPF, the 10-Lookup Cap, and the onmicrosoft.com Gotcha
A focused operator-implementation walkthrough for SPF on Microsoft 365 / Exchange Online tenants. The canonical SPF record for an M365 tenant sending only via Exchange Online is v=spf1 include:spf.protection.outlook.com -all — but most enterprise tenants have third-party senders (Mailchimp, SendGrid, Salesforce), hybrid on-prem + cloud topologies (Centralized Mail Transport, Direct-Delivery), the 10-lookup cap of RFC 7208 section 4.6.4 to budget against, and the <tenant>.onmicrosoft.com routing-domain gotcha that produces "SPF passes but DMARC fails for my custom domain" in aggregate reports. Documents the canonical record, the spf.protection.outlook.com include chain (flat ip4/ip6 enumeration, single realised lookup), the third-party-sender addition pattern with canonical includes for eight major services, hybrid SPF for CMT and Direct-Delivery topologies, the 10-lookup cap and three canonical mitigations (flattening, relay consolidation, subdomain partitioning), the onmicrosoft.com gotcha and its Set-Mailbox fix, a four-step verification workflow (dig, PowerShell Resolve-DnsName, Defender portal message trace, Email Header Analyzer), seven common mistakes operators ship in production, a per-symptom troubleshooting decision matrix, and four real-world examples. Differentiation from sibling guides: help_dmarc_for_microsoft_365.php covers SPF in Step 1 of a broader DKIM + DMARC deployment; this page is the SPF-only operator reference.
This reference is provided in English. Microsoft 365 Defender portal UI labels, PowerShell cmdlets (Resolve-DnsName, Set-Mailbox, Get-AcceptedDomain, Connect-ExchangeOnline), and DNS record syntax (v=spf1, include:spf.protection.outlook.com, -all, ip4) do not translate. We keep the body in the source language so technical terms remain consistent with Microsoft Learn documentation and RFC 7208.
TL;DR
- Canonical M365 SPF record:
v=spf1 include:spf.protection.outlook.com -allpublished as a TXT record at the apex of the sending domain. Use-all(hardfail) rather than~all(softfail) so receivers including Gmail apply DMARC enforcement uniformly. - Microsoft maintains the IP list. The
include:spf.protection.outlook.commechanism delegates SPF evaluation to a Microsoft-managed TXT record atspf.protection.outlook.com, which lists theip4andip6ranges of the Exchange Online outbound servers. Operators ride the central record by publishing the single include — never hardcode individual IPs. - Third-party senders go BEFORE the
Microsoft include. If you send via
Mailchimp, SendGrid, Mailgun, Salesforce, or any
other relay that sends AS your custom domain,
prepend their include / ip4 mechanism to the
SPF record before
include:spf.protection.outlook.com. Each include counts against the 10-lookup cap of RFC 7208 section 4.6.4. - Hybrid deployments need both paths.
Tenants with on-prem Exchange Server flowing
through an Edge Transport server or perimeter
SMTP gateway need an
ip4:<onprem-edge-ip-range>mechanism alongsideinclude:spf.protection.outlook.com. Centralized Mail Transport (CMT) topology routes through on-prem; Direct-Delivery uses both paths. - The
<tenant>.onmicrosoft.comSPF trap: Exchange Online routes outbound mail with the tenant'sonmicrosoft.comMAIL FROM by default. SPF passes (Microsoft's own record authorises every M365 IP) but DMARC alignment fails because the From: header organisational domain is your custom domain, notonmicrosoft.com. The fix is to set the custom domain as the primary email domain and reassign mailbox routing. - Stay under 10 lookups.
RFC 7208 section 4.6.4
caps SPF DNS lookups at 10. A bare
include:spf.protection.outlook.comconsumes 1 lookup (the include itself; the inner record is flatip4/ip6); third-party includes cost between 1 and 5 each. Verify withdig +shortor the Domain Security Checker before deploying. - Verify with four channels:
(1)
dig +short txt example.com, (2)dig +short txt spf.protection.outlook.com, (3) Defender portal message trace atsecurity.microsoft.com, and (4) the Email Header Analyzer on any received message. - Detection in DMARC aggregate (RUA)
reports: the Top Source IPs table in
the
DMARC Analyzer Dashboard
surfaces every IP claiming to send AS your
domain. Microsoft 365 outbound IPs from the
spf.protection.outlook.comranges should showspf=pass; any unfamiliar IP claimingMAIL FROM=<your-domain>withspf=failis either a forwarded message, an unauthorised relay, or a third-party sender you forgot to add to your SPF record.
1. Overview: what this guide covers
Microsoft 365 / Exchange Online is the dominant
enterprise mailbox provider — Microsoft reports
over 400 million paid M365 commercial seats as
of 2025, with Exchange Online carrying mail for
the bulk of that base. The SPF configuration for
an M365 tenant looks superficially trivial:
publish
v=spf1 include:spf.protection.outlook.com -all
at the apex of every sending domain and you are
done. In practice that is the right answer for
the simplest deployments — a single domain
sending only via Exchange Online with no
third-party services and no on-prem mail
infrastructure. Most enterprise tenants are not
that simple.
This page documents the SPF-specific operational
surface for Microsoft 365 / Exchange Online
deployments at the level operators actually
encounter in production: the canonical record and
why -all over ~all, the
spf.protection.outlook.com include
chain and its realised lookup count, the third-
party-sender addition pattern for Mailchimp,
SendGrid, Mailgun, Salesforce, Marketo, and other
relays sending AS your custom domain, hybrid
SPF for on-prem Exchange Server + Exchange Online
flows with Centralized Mail Transport and Direct-
Delivery topology, the
<tenant>.onmicrosoft.com SPF
alignment gotcha that produces the symptom "SPF
passes but DMARC fails for my custom domain",
the
RFC 7208 section 4.6.4
10-lookup cap risk on M365 deployments with many
third-party senders, PowerShell-based
verification with the
Resolve-DnsName cmdlet and Defender
portal message trace, and the common-mistake
punch list operators routinely ship in
production.
Differentiation from sibling guides: DMARC for Microsoft 365 covers SPF in Step 1 at the publish-the-record level alongside DKIM and DMARC deployment; SPF Explained is the protocol-agnostic SPF pillar; SPF Lookup Limit is the 10-lookup cap deep dive applicable to every SPF deployment; Forwarding Issues with SPF covers forwarding-breaks-SPF operationally; THIS page is the focused operator-implementation walkthrough for SPF on Microsoft 365 / Exchange Online.
This guide assumes operators are already familiar with basic SPF mechanics (SPF Explained) and DMARC fundamentals (DMARC Reference). For operators starting from zero, read those pages first; for operators with a running M365 tenant debugging an SPF-related symptom, start with the Troubleshooting decision matrix in Section 10.
2. The canonical Microsoft 365 SPF record
The canonical SPF record for a tenant sending mail ONLY via Exchange Online is a single TXT record at the apex of every sending domain:
example.com. IN TXT "v=spf1 include:spf.protection.outlook.com -all"
Every element of that record is intentional and documented in Microsoft Learn: Set up SPF to help prevent spoofing (the canonical primary-source Microsoft documentation for SPF on Exchange Online):
v=spf1: the mandatory SPF version tag per RFC 7208 section 4.5. The receiver MUST find this prefix to recognise the record as an SPF record.include:spf.protection.outlook.com: the Microsoft-managed include that delegates SPF evaluation to the centralspf.protection.outlook.comTXT record. Microsoft updates this record as it adds and retires outbound IPs; tenants ride the central record by publishing the single include.-all: the hardfail qualifier. Any sender whose IP is NOT on the authorised list (i.e., not in any ip4 / ip6 mechanism ofspf.protection.outlook.com) receivesspf=fail. Receivers including Gmail, Yahoo, Microsoft 365 itself, and Apple Mail apply DMARC enforcement (quarantine or reject) uniformly onfailverdicts.
Why -all rather than
~all? Microsoft recommends
-all for Microsoft 365 domains and gives the
reason: it also recommends DKIM and DMARC, and the DMARC
policy tells receivers what to do with messages that fail.
DMARC treats -all and ~all
alike, as an SPF failure. The Feb 2024 Google and Yahoo
bulk-sender requirements ask for SPF, DKIM and DMARC; they
do not prescribe a qualifier. Operators migrating from a
legacy ~all deployment can move to
-all once aggregate reports collected at
p=none no longer show unknown legitimate
sources.
Where do I publish it? At the
apex of every domain that appears as a From:
header organisational domain in mail your tenant
sends. If your users send AS
alice@example.com and
alice@subsidiary.example.com,
BOTH example.com and
subsidiary.example.com need their
own SPF record at the apex. Subdomain inheritance
does NOT apply to SPF — every From: domain stands
alone. (Subdomain inheritance DOES apply to
DMARC via the sp= tag, but that is
a DMARC-specific feature.) If your tenant uses a
common dotted parent domain
(parent.com) and several active
sending subdomains
(marketing.parent.com,
sales.parent.com,
notifications.parent.com), publish
SPF separately on each one.
Publication via DNS provider UIs: Most
DNS providers accept the literal SPF record body in their
TXT record form. Cloudflare, Route 53, Azure DNS, GoDaddy,
Namecheap, and Google Domains all support this. Some
legacy providers require the body to be wrapped in double
quotes; check your providers TXT record syntax. The
separate SPF record type (RFC 4408 type 99)
was discontinued by RFC 7208; use the standard
TXT record type universally.
3. The spf.protection.outlook.com include chain
The
include:spf.protection.outlook.com
mechanism delegates SPF evaluation to a
Microsoft-managed TXT record. To understand the
realised lookup count and the IP coverage, query
the record directly:
$ dig +short txt spf.protection.outlook.com
"v=spf1 ip4:40.92.0.0/15 ip4:40.107.0.0/16 ip4:52.100.0.0/15 ip4:52.102.0.0/16 ip4:52.103.0.0/17 ip4:104.47.0.0/17 ip6:2a01:111:f400::/48 ip6:2a01:111:f403::/49 ip6:2a01:111:f403:8000::/51 ip6:2a01:111:f403:c000::/51 ip6:2a01:111:f403:f000::/52 -all"
(The exact IP ranges rotate as Microsoft adds
and retires datacenter capacity; the snapshot
above was captured in 2026 and the operator
should run their own live dig
against spf.protection.outlook.com
for the current authoritative list.)
The record is flat: it contains only v=spf1,
a sequence of ip4 and ip6
mechanisms, and the -all terminator. There
are no nested includes, no a /
mx mechanisms, no redirect, and
no SPF macros. It was not always so: the record used to
point on to the records
spfa.protection.outlook.com and
spfb.protection.outlook.com, so it cost more
than one lookup. Both names still resolve, but the current
record no longer uses them.
Realised lookup count: when an
operator publishes
include:spf.protection.outlook.com
in their own record, the receivers SPF evaluator
counts:
- 1 lookup for the
include:spf.protection.outlook.commechanism itself (per RFC 7208 section 4.6.4 eachincludeis one DNS lookup). - 0 additional lookups inside the include — the
spf.protection.outlook.comrecord contains onlyip4andip6mechanisms which do NOT consume lookup slots.
So the Microsoft include consumes a single realised lookup. The operators 10-lookup budget is therefore 9 remaining lookups for any other mechanisms they add to the record. This is the headroom available for third-party senders.
Historical complications: an inherited
SPF record may contain direct references to
spfa.protection.outlook.com or
spfb.protection.outlook.com, entered by hand
in place of the main name, or Microsoft address ranges
copied as ip4 entries (see section 9.5).
Replace them with the single
include:spf.protection.outlook.com: it is the
only name Microsoft documents for the commercial service,
and the record behind it,
spf.protection.outlook.com, is the one
Microsoft keeps current.
Geo-specific includes: the
spf.protection.outlook.com record
covers ALL Exchange Online regions globally.
Microsoft does not publish region-specific SPF
records (no spf-eu.protection.outlook.com
/ spf-us.protection.outlook.com
variants) because the same outbound IPs may be
used from any region depending on routing. Do
NOT attempt geo-specific SPF configurations —
publish the universal include and rely on
Microsoft to maintain the global IP list.
4. Adding third-party senders to your Microsoft 365 SPF
Most enterprise tenants do not send mail ONLY via
Exchange Online. They also use third-party
services that send AS the tenants custom domain:
marketing automation (Mailchimp, HubSpot, Marketo,
Eloqua), transactional email (SendGrid, Mailgun,
Postmark, Amazon SES), CRM (Salesforce, Microsoft
Dynamics 365), survey tools (SurveyMonkey,
Qualtrics), and ticketing systems (Zendesk,
ServiceNow, Freshdesk). Each of these services
needs to be authorised in your SPF record OR
they will produce spf=fail when
receivers check messages sent through them.
The canonical addition pattern is to prepend each
third-party senders include or ip4 mechanism
BEFORE the include:spf.protection.outlook.com
mechanism, separated by single spaces. Example
record for a tenant using Mailchimp and SendGrid
alongside Exchange Online:
example.com. IN TXT "v=spf1 include:servers.mcsv.net include:sendgrid.net include:spf.protection.outlook.com -all"
Does the order matter? Not for the result, but for the cost: receivers evaluate the mechanisms from left to right and stop at the first match, as Microsoft notes in its documentation. Put the source that sends most of your mail first. Microsoft also recommends giving services you do not control directly a subdomain with its own SPF record instead of adding them to the main domain.
4.1 Mailchimp (Intuit Mailchimp)
Sender: Mailchimp / Intuit
Mailchimp — the dominant marketing-automation
platform sending transactional and bulk marketing
mail AS the tenants custom domain.
Why it needs SPF: Mailchimp
sends from Mailchimp-owned IP ranges
(198.2.128.0/18 and others), not
Exchange Online IPs — receivers checking
MAIL FROM=<your-domain> against
your SPF record will produce
spf=fail unless Mailchimp is
authorised.
Canonical fix: add
include:servers.mcsv.net to the SPF
record. The Mailchimp documentation prescribes
this include and Mailchimp maintains the central
servers.mcsv.net record. As of 2026
the include consumes 1 realised lookup.
4.2 SendGrid (Twilio SendGrid)
Sender: SendGrid / Twilio SendGrid — the
dominant transactional-email platform.
Why it needs SPF: SendGrid delivers from its own IP
ranges (167.89.0.0/17,
168.245.0.0/17, and others) independent of
Exchange Online.
Canonical fix: add
include:sendgrid.net to the SPF record.
SendGrid maintains the central sendgrid.net
record. Counts as 2 lookups: on 12 October 2026 the record
contained one nested include.
4.3 Mailgun (Sinch Mailgun)
Sender: Mailgun / Sinch Mailgun —
alternative transactional-email platform.
Why it needs SPF: Mailgun sends from
mailgun.org-owned IP ranges.
Canonical fix: add
include:mailgun.org to the SPF record. Counts
as 5 lookups: on 12 October 2026 the record pointed to two
further records, one of which pointed to two more.
4.4 Salesforce
Sender: Salesforce — CRM
platform that sends notification mail, marketing
mail (via Marketing Cloud / Pardot), and
transactional mail (via Service Cloud) AS the
tenants domain.
Why it needs SPF: Salesforce
sends from Salesforce-owned IP ranges that
depend on the customers instance (NA / EU /
AP). The instance-routing makes static IPv4
enumeration impractical.
Canonical fix: add
include:_spf.salesforce.com to the
SPF record. Consumes typically 1-2 realised
lookups depending on instance.
4.5 Marketo (Adobe Marketo Engage)
Sender: Marketo / Adobe Marketo
Engage — enterprise marketing-automation
platform.
Why it needs SPF: Marketo
sends from Adobe-owned IP ranges separate from
Exchange Online.
Canonical fix: add
include:mktomail.com to the SPF
record. Adobe maintains the central
mktomail.com record.
4.6 Amazon SES
Sender: Amazon SES — AWS
transactional-email service.
Why it needs SPF: SES sends
from amazonses.com IP ranges that
depend on the AWS region.
Canonical fix: add
include:amazonses.com to the SPF
record. Consumes 1 realised lookup. SES also
supports a DKIM-only flow where the SPF
addition can be skipped if DMARC alignment
relies entirely on DKIM, but the canonical
deployment publishes both SPF and DKIM
authorisation.
4.7 Zendesk
Sender: Zendesk — customer
support / ticketing platform that sends
ticket-reply mail AS the tenants support@
address.
Why it needs SPF: Zendesk
sends through SendGrid backend — without an
SPF authorisation the ticket replies fail SPF
at the customers receiving server.
Canonical fix: add
include:mail.zendesk.com to the
SPF record OR rely on the existing SendGrid
include if Zendesk customers share the SendGrid
backend (Zendesk Enterprise plans).
4.8 HubSpot
Sender: HubSpot — marketing-
automation + CRM platform.
Why it needs SPF: HubSpot
sends marketing mail from HubSpot-owned IP
ranges.
Canonical fix: historical
pattern was include:_spf.google.com
(HubSpot ran on Google Workspace backend),
modern pattern is include:hubspotemail.net
after HubSpot migrated to its own infrastructure
around 2020. Confirm the current canonical
include against the HubSpot Help Center
documentation for your portal.
The eight services above cover the bulk of enterprise
third-party SPF additions, but the number of includes says
nothing about the number of lookups. Added to one record
in full, the eight would have cost 1 + 2 + 5 + 2 + 1 + 1 +
1 + 1 = 14 lookups on 12 October 2026, and 15 with
include:spf.protection.outlook.com — well
over the cap. Tenants needing many services should split
them across subdomains, send through Exchange Online where
possible, or consider SPF flattening. See the
SPF Lookup Limit deep dive.
5. Hybrid Microsoft 365 + on-prem Exchange Server SPF
A hybrid Microsoft 365 + on-prem Exchange Server deployment routes mail through BOTH Exchange Online AND the on-prem Exchange organisation depending on:
- The recipients mailbox location (on-prem mailbox vs M365 cloud mailbox).
- The migration batch state during a Cloud-Onboarding migration.
- The chosen mail routing topology (Centralized Mail Transport / Direct- Delivery / Hybrid Modern Authentication).
5.1 Centralized Mail Transport (CMT)
Centralized Mail Transport (CMT) topology routes ALL outbound mail through the on-prem Exchange organisation first. Cloud mailboxes send via Exchange Online to the on-prem Hybrid Configuration Wizard receive connector, and the on-prem Edge Transport server (or perimeter SMTP gateway) sends the mail outbound. The canonical hybrid SPF record for a CMT deployment is:
example.com. IN TXT "v=spf1 ip4:198.51.100.0/29 include:spf.protection.outlook.com -all"
Where 198.51.100.0/29 is the
public IP range of the on-prem Edge Transport
server or perimeter SMTP gateway. Technically
the include:spf.protection.outlook.com
is unnecessary in pure CMT (every outbound
message comes from the on-prem range), but
most operators keep it for migration-state
safety — during the migration any cloud
mailbox that has not yet been re-routed
through CMT will send directly from Exchange
Online and needs the Microsoft include.
5.2 Direct-Delivery
Direct-Delivery topology lets Exchange Online send mail for cloud-hosted mailboxes directly without routing through on-prem. Mail for on-prem mailboxes still uses the on-prem Edge Transport server. The canonical SPF record must authorise BOTH outbound paths:
example.com. IN TXT "v=spf1 ip4:198.51.100.0/29 include:spf.protection.outlook.com -all"
The record is identical to the CMT case above
— both topologies require both
ip4:<onprem> and
include:spf.protection.outlook.com.
The difference is purely operational: CMT
funnels all outbound through one IP range,
Direct-Delivery uses both paths simultaneously.
5.3 The Hybrid Configuration Wizard and DNS
The Hybrid Configuration Wizard (HCW) is the
Microsoft tool that configures the on-prem
Exchange Hybrid Server + the Exchange Online
receive / send connectors for the hybrid
deployment. The HCW does NOT update
the operators public DNS — it
configures the on-prem Exchange organisation
and the Exchange Online side of the trust
relationship, but the SPF record at the
operators DNS provider is a separate manual
step the operator must perform. The most
common hybrid SPF mistake is finishing the
HCW and forgetting to update the SPF record;
cloud-mailbox outbound mail then either fails
SPF (if the operator removed
include:spf.protection.outlook.com)
or the on-prem Edge Transport sender fails
SPF (if the operator never added the on-prem
IP range).
5.4 Secondary on-prem mail flows
Enterprise tenants often have additional on-prem
mail-sending infrastructure beyond Exchange Server: HR
systems (Workday, SAP SuccessFactors), ERP systems (SAP,
Oracle, NetSuite), monitoring systems (Nagios, PRTG, SCOM
sending alerts), printer / scanner / MFD devices sending
Scan-to-Email, and legacy SMTP relays. Each of these
sources needs to be inventoried and authorised. The
canonical pattern is to consolidate them all behind a
single SMTP relay (the on-prem Edge Transport or a
dedicated relay like Postfix at the perimeter) and
authorise the single relays IP range in the SPF record.
Authorising each legacy SMTP source individually with
ip4: entries costs no lookups, but it
lengthens the record, makes it harder to maintain, and
leaves entries behind long after the device is gone.
6. The 10-lookup cap on Microsoft 365 SPF
RFC 7208 section 4.6.4 caps the number of DNS lookups an SPF evaluation can perform at 10. The cap exists to prevent denial-of-service via deeply nested SPF records that would otherwise force the receiver to perform arbitrary DNS work for every incoming message. The cap counts:
- Each
includemechanism (1 lookup each). - Each
amechanism (1 lookup each). - Each
mxmechanism (1 lookup each). - Each
ptrmechanism (1 lookup each, plus the reverse lookup — most operators avoidptras it is deprecated by RFC 7208 itself). - Each
existsmechanism (1 lookup each). - Each
redirectmodifier (1 lookup each).
The ip4 and ip6
mechanisms do NOT consume lookup slots — they
are literal IP comparisons. The
all mechanism also does not
consume a lookup.
The Microsoft 365 baseline:
include:spf.protection.outlook.com
consumes 1 lookup (the include itself) and 0
nested lookups (the inner record is flat
ip4/ip6). The
operators 10-lookup budget is therefore 9
remaining lookups after the Microsoft
include.
Worked example — 9 third-party senders: a tenant using Mailchimp, SendGrid, Mailgun, Salesforce, Marketo, Amazon SES, Zendesk, HubSpot, and one custom relay publishes:
example.com. IN TXT "v=spf1 include:servers.mcsv.net include:sendgrid.net include:mailgun.org include:_spf.salesforce.com include:mktomail.com include:amazonses.com include:mail.zendesk.com include:hubspotemail.net ip4:198.51.100.10 include:spf.protection.outlook.com -all"
Lookup accounting: 8 third-party includes (8 lookups) + 0
lookups for ip4 + 1 lookup for the Microsoft
include = 9 top-level lookups. Plus the nested lookups
inside the third-party includes: on 12 October 2026 there
were 1 in the SendGrid record, 4 in the Mailgun record and
1 in the Salesforce record. Total 15. A receiver that has
to walk the whole record returns
spf=permerror, and that result does not count
for DMARC. Because evaluation stops at the first match,
mail from the sources listed first may still pass while
mail from those listed later does not, which makes the
fault look random.
How to verify your realised count:
- Run a recursive SPF resolver tool. The
Domain Security
Checker at
domain_checker.phpon this site runs the full SPF chain viaincludes/DnsChecker.phpand surfaces the realised lookup count. - Or run
dig +short txt example.comand then recursivelydig +short txt <each-included-domain>— counting every include, a, mx, ptr, exists, and redirect encountered. - Or do the same in PowerShell with
Resolve-DnsName, name by name. Microsoft 365 has no cmdlet that manages or tests the SPF record of your domain; the record lives at your DNS provider, so query theTXTrecords directly:
PS> Resolve-DnsName -Type TXT -Name example.com | Where-Object {$_.Strings -like 'v=spf1*'}
PS> Resolve-DnsName -Type TXT -Name sendgrid.net | Where-Object {$_.Strings -like 'v=spf1*'}
Mitigations when over the cap:
- SPF flattening: replace third-party includes with direct ip4 / ip6 ranges. Tools like SPFLab and EasyDMARC automate the flattening; the trade-off is the operator must re-flatten regularly as senders update their IP ranges. Most operators avoid manual flattening because of the maintenance burden.
- Consolidate via Exchange Online as a relay: configure third-party services to send through Exchange Online via SMTP AUTH or via a connector. All outbound mail then comes from Exchange Online IPs and consumes only the single Microsoft include. Trade-off: operators must configure each third-party service to use Exchange Online as a relay, and Exchange Online receive connectors require a service account or certificate-based authentication.
- Subdomain partitioning:
publish separate SPF records on different
subdomains for different sending services.
For example,
marketing.example.compublishesv=spf1 include:servers.mcsv.net include:mktomail.com -allfor marketing-mail-only senders andtransactional.example.compublishesv=spf1 include:sendgrid.net include:amazonses.com -all. Each subdomain has its own 10-lookup budget. The operator must then configure each third-party service to use the correct subdomain in MAIL FROM.
See the SPF Lookup Limit deep dive for the full treatment of the cap and its mitigations.
7. The <tenant>.onmicrosoft.com SPF gotcha
Every Microsoft 365 tenant is assigned a default
routing domain of the form
<tenant>.onmicrosoft.com at
tenant creation. The onmicrosoft.com
parent domain is owned and operated by Microsoft;
it publishes its own SPF record that authorises
every M365 outbound IP. The
<tenant>.onmicrosoft.com name
inherits Microsofts SPF via the parent record.
SPF passes for any message with MAIL FROM
<user>@<tenant>.onmicrosoft.com.
The gotcha is that Exchange
Online routes outbound messages with the
<tenant>.onmicrosoft.com
MAIL FROM by default, even when the user has
added a custom domain (example.com)
to the tenant and is sending mail with From:
header alice@example.com. The
symptoms operators see:
- SPF passes (Microsofts own record authorises every M365 IP).
- DKIM passes IF the tenant has configured DKIM for the custom domain.
- DMARC fails with
spf-aligned=failbecause the MAIL FROM domain (<tenant>.onmicrosoft.com) does NOT align with the From: header organisational domain (example.com). - If DKIM is aligned, DMARC may still pass via DKIM alignment alone. If DKIM is NOT aligned, DMARC fails entirely.
The aggregate (RUA) report symptom is:
spf=pass against
<tenant>.onmicrosoft.com
but aligned-spf=fail against the
custom domain. The same source IP. The same
message. SPF is "passing" but DMARC fails.
This pattern is the dominant DMARC failure
mode for tenants that have not properly
configured the custom domain as the primary
email domain.
7.1 Why does this happen?
Three causes converge:
- The default routing domain assignment.
Newly created mailboxes are assigned
<user>@<tenant>.onmicrosoft.comas the primary email address until the custom domain is set as primary. - Service accounts and shared mailboxes.
These often remain on
onmicrosoft.combecause they were created before the custom domain was added or because no operator explicitly switched them. - App-only authentication. Applications using
OAuth or SMTP AUTH with service-account
credentials may send AS the service
accounts
onmicrosoft.comaddress even when the application is configured to use a custom From: header.
7.2 How to fix it
The canonical fix is to set the custom domain as the primary email domain in the tenant AND reassign every mailboxs primary email address to the custom domain:
PS> Connect-ExchangeOnline
PS> Set-Mailbox -Identity alice@contoso.onmicrosoft.com `
-PrimarySmtpAddress alice@example.com `
-EmailAddresses @{Add="smtp:alice@contoso.onmicrosoft.com"}
This sets the custom domain
(example.com) as the primary
address while keeping the
onmicrosoft.com address as an
alias (so existing app integrations continue
to work). After running this command, the
mailbox sends with MAIL FROM
alice@example.com and DMARC
alignment succeeds.
For service accounts and shared mailboxes, repeat the same pattern. For app-only senders, verify the application is configured to use the custom domain as the From: address AND that any SMTP AUTH credentials use a mailbox whose primary address is on the custom domain.
7.3 How to detect the pattern in aggregate reports
The
DMARC Analyzer Dashboard
surfaces the per-source-IP / per-MAIL-FROM
breakdown in the Top Source IPs table and the
Auth Results column. The
<tenant>.onmicrosoft.com
pattern shows up as:
- Source IP in the
spf.protection.outlook.comranges (Microsoft outbound IPs). - SPF result
passagainst<tenant>.onmicrosoft.com. - SPF-aligned result
failagainst the published custom domain. - DKIM result
passagainst the custom domain (if DKIM is configured). - DMARC verdict depends on DKIM alignment — if DKIM aligns, DMARC passes; if not, DMARC fails.
Click any row in the dashboard to see the per-record breakdown showing the MAIL FROM domain explicitly.
8. Verification workflow
The canonical four-step verification for Microsoft 365 SPF works for any tenant regardless of size or topology:
8.1 Step 1: Verify the record is published
From any workstation with dig
(Linux / macOS / WSL) or nslookup
(Windows):
$ dig +short txt example.com
"v=spf1 include:spf.protection.outlook.com -all"
Or in PowerShell using the
Resolve-DnsName cmdlet (Windows
10/11 and Windows Server):
PS> Resolve-DnsName -Type TXT -Name example.com | Where-Object {$_.Strings -like 'v=spf1*'}
Verify (a) exactly one SPF record exists
(multiple SPF records on the same domain
produce spf=permerror per RFC
7208 section 4.5), (b) the record contains
include:spf.protection.outlook.com,
and (c) the record terminates with
-all (or ~all
during migration).
8.2 Step 2: Verify Microsofts include chain
$ dig +short txt spf.protection.outlook.com
"v=spf1 ip4:40.92.0.0/15 ip4:40.107.0.0/16 ip4:52.100.0.0/15 ip4:52.102.0.0/16 ip4:52.103.0.0/17 ip4:104.47.0.0/17 ip6:2a01:111:f400::/48 ip6:2a01:111:f403::/49 ip6:2a01:111:f403:8000::/51 ip6:2a01:111:f403:c000::/51 ip6:2a01:111:f403:f000::/52 -all"
Confirm the record resolves (no NXDOMAIN or
SERVFAIL) and contains the expected flat
ip4 / ip6
enumeration. Microsoft retires and adds
ranges over time; the exact IPs you see may
differ from the snapshot above.
8.3 Step 3: Microsoft 365 message trace
Send a test message from your tenant to an
external mailbox (a Gmail or Yahoo account
you control is ideal). Then in the Defender
portal at
security.microsoft.com:
- Navigate to Email & collaboration > Exchange message trace.
- Search for the test message by sender, recipient, or subject.
- Click the message to open the detailed view.
- Confirm that the message was delivered externally and
by which route it left. The SPF result itself is
recorded by the receiver: look for
spf=pass smtp.mailfrom=<your-domain>in the headers of the received message (step 4).
8.4 Step 4: Email Header Analyzer
Inspect the actual message headers as the
receiver saw them. Open the test message in
Gmail / Yahoo / Apple Mail and view the
raw source headers (View Original / Show
Original). Paste the headers into the
Email Header
Analyzer at
header_analyzer.php on this
site — it parses the full
Authentication-Results stack via
includes/EmailHeaderParser.php
and surfaces the SPF, DKIM, DMARC, and ARC
verdicts in human-readable form. Confirm
SPF passes against the expected MAIL FROM
domain.
8.5 Step 5: Domain Security Checker (live lookup count)
The
Domain Security
Checker at
domain_checker.php on this
site runs the full SPF chain via
includes/DnsChecker.php and
surfaces the realised lookup count. This is
the canonical way to check you remain under
the 10-lookup cap of RFC 7208 section 4.6.4.
The tool also detects multiple SPF records
(the spf=permerror cause documented in
SPF
permerror vs temperror), missing
-all terminators, and deprecated
ptr mechanisms.
9. Common Microsoft 365 SPF mistakes
9.1 Multiple SPF records on the same domain
Publishing two TXT records both starting with
v=spf1 on the same domain
produces spf=permerror per
RFC 7208 section 4.5.
The most common cause is operators publishing
one SPF record for Exchange Online and a
second SPF record for a third-party sender,
intending to "combine" them but creating two
records instead of one merged record. The fix
is to delete one record and merge all
mechanisms into a single record. Verify with
dig +short txt example.com —
exactly one line beginning with
v=spf1 should appear.
9.2 Using ~all when -all is required
Soft-fail (~all) asks receivers to accept
mail from unauthorised sources but mark it; hard-fail
(-all) asks them to reject it. What actually
happens depends on the receiver. Microsoft recommends
-all for Microsoft 365 domains and notes that
DMARC treats both results as an SPF failure. The Feb 2024
Google and Yahoo bulk-sender requirements do not prescribe
a qualifier. The mistake is therefore not
~all as such, but leaving it in place with no
plan to move to -all and without the DKIM and
DMARC that actually protect the domain.
9.3 Publishing the SPF record on the wrong name
The SPF record MUST be published at the apex
of each From: domain
(example.com), not on a subdomain
intended for sending (e.g.,
mail.example.com — that would
only authorise mail with MAIL FROM in the
mail. subdomain). The most
common variant of this mistake is publishing
the SPF record at
_spf.example.com or
spf.example.com by analogy with
DKIMs _domainkey convention.
SPF does not use a label prefix — publish
directly at the apex.
9.4 Improper TXT record quoting
Some DNS providers require the SPF body to be enclosed in
double quotes; others apply quoting automatically. The
dangerous form is "v=spf1"
"include:spf.protection.outlook.com" "-all", three
separate strings in one record. RFC 7208 joins such
strings with no space between them, so the receiver sees
one unbroken token and the record is no longer a valid SPF
record. Publish a single quoted string: "v=spf1
include:spf.protection.outlook.com -all". If a
record is longer than 255 characters and has to be split,
the space must sit inside one of the strings.
9.5 Hardcoding Microsoft IP ranges in ip4:
Tempting shortcut: replace
include:spf.protection.outlook.com
with a hardcoded
ip4:40.92.0.0/15 ip4:40.107.0.0/16 ...
list to "save lookups." This breaks the moment
Microsoft adds a new IP range — every legitimate
Exchange Online message from the new range fails
SPF. Never hardcode Microsoft IPs. The
include mechanism delegates IP
maintenance to Microsoft; that delegation is
the entire point of the central
spf.protection.outlook.com record.
9.6 Forgetting to add third-party senders
After deploying Mailchimp / SendGrid /
Salesforce / etc. for a marketing campaign or
transactional flow, operators commonly forget
to update the SPF record. The senders mail
deploys, the third-party service sends from
its own IPs, and receivers return
spf=fail — driving the marketing
mail into spam folders. The fix is to add the
include BEFORE the campaign launches. Use the
Domain Security
Checker to verify the SPF record contains
the expected includes for every active third-
party service.
9.7 Removing include:spf.protection.outlook.com during hybrid migration
During an on-prem-to-cloud migration,
operators sometimes remove
include:spf.protection.outlook.com
"because all mail flows through on-prem now."
But some cloud-hosted mailboxes are still
delivering directly via Exchange Online (the
HCW transition state). Their outbound mail
starts failing SPF. The safe pattern is to
keep include:spf.protection.outlook.com
in the SPF record throughout the migration
and only consider removing it years after
100% of mail flow has been confirmed routed
through on-prem via aggregate (RUA) report
analysis.
10. Troubleshooting decision matrix
Symptom-to-cause lookup table for the common Microsoft 365 SPF issues operators encounter:
| Symptom | Likely cause | Section reference |
|---|---|---|
spf=permerror on
aggregate reports |
Either multiple SPF records on the same domain OR > 10 lookups exhausted | Section 6 (10-lookup cap), Section 9.1 (multiple records) |
spf=temperror
intermittently |
DNS resolver timeout or SERVFAIL — typically a transient resolver issue, retried by the receiver | SPF permerror vs temperror |
SPF passes but DMARC fails
aligned-spf=fail |
MAIL FROM is
<tenant>.onmicrosoft.com,
not the custom domain |
Section 7 (onmicrosoft.com gotcha) |
spf=fail on
forwarded mail |
Forwarding is a second SMTP hop from a different IP that the original senders SPF does NOT authorise | SPF Forwarding Issues |
spf=fail on mail
sent through Mailchimp /
SendGrid / etc. |
The third-party sender is NOT in the SPF record | Section 4 (third-party senders) |
spf=fail on
hybrid-deployment on-prem-sent
mail |
The on-prem Edge Transport IP range is NOT in the SPF record | Section 5 (hybrid SPF) |
spf=none result |
No SPF record published at the apex of the From: domain | Section 2 (canonical record) |
| Mail flow appears to succeed but does not reach Gmail / Yahoo recipients | Likely DMARC enforcement at receiver because Feb 2024 bulk-sender requirements not met — verify SPF, DKIM, DMARC, one-click unsubscribe | DMARC for Microsoft 365 |
For symptoms not covered above, start with the Email Header Analyzer on a representative failing message to see the full Authentication- Results stack, then drill into the matching section above.
11. Real-world examples
11.1 Startup with only Exchange Online (10 employees)
A 10-person startup uses Microsoft 365 Business Basic. All outbound mail flows through Exchange Online. No third-party senders. The canonical SPF record is simply:
example.io. IN TXT "v=spf1 include:spf.protection.outlook.com -all"
Realised lookup count: 1. Room for 9 more if needed. SPF deployment time: 5 minutes including DNS propagation verification.
11.2 SaaS company with marketing automation (200 employees)
A SaaS company uses Exchange Online for employee mail, Mailchimp for newsletters, SendGrid for transactional product notifications, and Salesforce for sales outreach. Canonical SPF record:
example.com. IN TXT "v=spf1 include:servers.mcsv.net include:sendgrid.net include:_spf.salesforce.com include:spf.protection.outlook.com -all"
Top-level lookups: 4. On 12 October 2026 the SendGrid record and the Salesforce record each added one nested lookup, bringing the total to 6. Under the 10-lookup cap, with room for a few more services.
11.3 Enterprise hybrid deployment (5000 employees)
A 5000-employee enterprise runs a hybrid
Exchange Server 2019 + Exchange Online
deployment with Centralized Mail Transport
(CMT). Mail flows: cloud mailboxes send via
Exchange Online to the on-prem Edge Transport
server at
198.51.100.0/29, which delivers
outbound. The company also uses Marketo for
enterprise marketing, ServiceNow for ITSM
ticket notifications, and Workday for HR
notifications. Canonical SPF record:
example.com. IN TXT "v=spf1 ip4:198.51.100.0/29 include:mktomail.com include:servicenowservices.com include:_spf.workday.com include:spf.protection.outlook.com -all"
Realised top-level lookups: 4 (one ip4 + three third-party includes + Microsoft include — ip4 does not count). Nested lookups inside the third-party includes typically add 3-5 more, bringing the total to 7-9. At the edge of the cap; the operator must monitor the lookup count quarterly and consider subdomain partitioning if any additional senders are added.
11.4 Migration: on-prem Exchange to M365 (Wave 5)
An organisation is migrating from on-prem Exchange Server 2016 to Microsoft 365. Wave 5 of the migration includes the Marketing team (50 mailboxes). Before the migration the SPF record was:
example.org. IN TXT "v=spf1 ip4:203.0.113.0/29 -all"
After Wave 5 the SPF record must authorise BOTH the legacy on-prem IP range AND the Exchange Online range for the migrated mailboxes that may send directly:
example.org. IN TXT "v=spf1 ip4:203.0.113.0/29 include:spf.protection.outlook.com -all"
After the migration is fully complete (all
mailboxes confirmed cloud-hosted and on-prem
Exchange decommissioned), the
ip4:203.0.113.0/29 mechanism
can be removed.
12. Frequently asked questions
- What is the canonical SPF record for Microsoft 365?
-
The canonical SPF record for a Microsoft 365 / Exchange Online tenant that sends mail ONLY via Exchange Online is
v=spf1 include:spf.protection.outlook.com -allpublished as a TXT record at the apex of the sending domain. Theinclude:spf.protection.outlook.commechanism delegates SPF evaluation to the Microsoft-managedspf.protection.outlook.comrecord, which currently expands to roughly sixip4ranges and oneip6range covering every Exchange Online outbound IP — Microsoft maintains the record and adds new IPs as the service grows, so the operator never has to track individual IP addresses. The-allqualifier hardfails any sender whose IP is not on the Microsoft list. Use-allrather than~all(softfail) so receivers including Gmail apply DMARC enforcement uniformly; Microsoft itself recommends-allin the canonical Microsoft Learn documentation. Operators sending via third-party services (Mailchimp, SendGrid, Salesforce) must add those services BEFORE theinclude:spf.protection.outlook.commechanism, watching the 10-lookup cap of RFC 7208 section 4.6.4. - Why does Microsoft 365 use include:spf.protection.outlook.com instead of an ip4 range?
-
Microsoft 365 / Exchange Online runs on a large and dynamic IP pool that changes as Microsoft scales the service, adds Azure regions, and migrates customers between datacenters. A hardcoded
ip4mechanism would require every tenant operator to manually update the SPF record every time Microsoft added or retired an outbound IP — an operational impossibility at the scale of hundreds of millions of M365 mailboxes. Theinclude:spf.protection.outlook.commechanism delegates SPF evaluation to a Microsoft-managed TXT record atspf.protection.outlook.comthat Microsoft updates centrally. Tenants ride the central record by publishing a single include mechanism. The trade-off is that thespf.protection.outlook.comrecord currently consumes roughly 4 of the 10 RFC 7208 section 4.6.4 lookup slots (the include itself + the ip4/ip6 mechanisms inside expand to one lookup each up to the 10- lookup cap), so operators adding third-party senders must budget the remaining 6 slots carefully. - How do I add Mailchimp or SendGrid to my Microsoft 365 SPF record?
-
Add the third-party senders include BEFORE the
include:spf.protection.outlook.commechanism, separated by a single space. Example:v=spf1 include:servers.mcsv.net include:sendgrid.net include:spf.protection.outlook.com -alladds Mailchimp (servers.mcsv.net) and SendGrid (sendgrid.net) while keeping Exchange Online authorised. Each include costs at least one DNS lookup against the RFC 7208 section 4.6.4 10-lookup cap, and nested includes count as well: on 12 October 2026 servers.mcsv.net cost 1 lookup and sendgrid.net cost 2, so the record above cost 4 in total with the Microsoft include. Run the live SPF lookup count viadig +shortor via the Domain Security Checkerdomain_checker.phptool BEFORE deploying to verify you remain under the 10-lookup cap. If the count exceeds 10 receivers including Gmail and Microsoft returnspf=permerrorand DMARC alignment fails. - What is the spf.protection.outlook.com lookup count?
-
The
spf.protection.outlook.comrecord currently consumes approximately 4 of the 10 RFC 7208 section 4.6.4 lookup slots as of 2026 (the actual realised count varies as Microsoft updates the record). The chain is: the operator publishesinclude:spf.protection.outlook.com— that is 1 lookup. Microsoft resolves thespf.protection.outlook.comTXT record — that is the cost of the include itself (counted). Thespf.protection.outlook.comrecord begins withv=spf1 ip4:40.92.0.0/15 ip4:40.107.0.0/16 ip4:52.100.0.0/14 ip4:104.47.0.0/17 ip6:2a01:111:f400::/48 -all(the IP list rotates as Microsoft updates) and contains onlyip4/ip6/-allmechanisms which do NOT consume additional lookups. So the M365 include itself is operationally close to a single- lookup cost. The record used to point on to the recordsspfa.protection.outlook.comandspfb.protection.outlook.comand cost more than one lookup; today it is flat. Operators should still run the live count viadig +shortordomain_checker.phpto verify current behaviour. - What is the onmicrosoft.com SPF problem?
-
Every Microsoft 365 tenant is assigned a default
<tenant>.onmicrosoft.comrouting domain. When operators add a custom domain (example.com) to the tenant but do not properly configure outbound mail to send AS the custom domain, Exchange Online routes outbound messages with MAIL FROM<user>@<tenant>.onmicrosoft.com— even though the visible From: header is<user>@example.com. The SPF check at the receiver authenticates the MAIL FROM domain, which is<tenant>.onmicrosoft.com— that lookup resolves to Microsoftsonmicrosoft.comrecord which is permissive of all M365 IPs, so SPF passes. But DMARC alignment requires the MAIL FROM domain to align with the From: header organisational domain, andonmicrosoft.comdoes NOT align withexample.com— DMARC fails withspf-aligned=fail. The symptom is "SPF passes but DMARC fails for my custom domain" in aggregate reports. The fix is to configure the custom domain as the primary email domain in the tenant and to set the default routing per-mailbox so MAIL FROM matches the From: header. Mailboxes assigned theonmicrosoft.comaddress by default require explicit reassignment viaSet-Mailbox -EmailAddressesor the Exchange Admin Center. - How do I configure SPF for a hybrid Microsoft 365 + on-prem Exchange deployment?
-
A hybrid Microsoft 365 + on-prem Exchange Server deployment routes mail through both Exchange Online AND the on-prem Exchange organisation depending on the recipients mailbox location, the migration batch state, and the chosen mail routing topology (Centralized Mail Transport vs Direct-Delivery). The SPF record must authorise BOTH outbound paths. The canonical hybrid SPF record is
v=spf1 ip4:<onprem-edge-ip-range> include:spf.protection.outlook.com -allwhere<onprem-edge-ip-range>is the public IP or /29 range of the on-prem Exchange Edge Transport server or perimeter SMTP gateway. Centralized Mail Transport (CMT) topology routes all outbound mail through the on-prem Exchange organisation first — in that case the on-prem IP range is the EXCLUSIVE sending source and theinclude:spf.protection.outlook.comis technically unnecessary, but most operators keep it for migration- state safety. Direct-Delivery topology lets Exchange Online send mail for cloud-hosted mailboxes directly without routing through on-prem — in that case BOTHip4:<onprem>andinclude:spf.protection.outlook.comare required. The Hybrid Configuration Wizard does NOT update the operators public DNS; the SPF update is a manual step the operator must perform at the DNS provider. - How do I verify my Microsoft 365 SPF record is working?
-
Run a four-step verification: (1)
dig +short txt example.com— confirms the record is published at the apex withv=spf1 include:spf.protection.outlook.com -all(or your hybrid variant). (2)dig +short txt spf.protection.outlook.com— confirms Microsofts include chain resolves and shows the currentip4/ip6ranges. (3) Microsoft 365 message trace via the Defender portal atsecurity.microsoft.com> Email & collaboration > Exchange message trace, search for a message you sent, click the message, look for the SPF result in the Authentication-Results header in the message details — should showspf=pass smtp.mailfrom=<your-domain>. (4) PowerShell from a workstation with theExchangeOnlineManagementmodule installed:Connect-ExchangeOnline; Get-AcceptedDomain— confirms the domain is registered to the tenant. The Domain Security Checkerdomain_checker.phptool runs the same DNS lookups throughincludes/DnsChecker.phpand surfaces the live realised lookup count, helping you spot the 10-lookup cap risk early. The Email Header Analyzerheader_analyzer.phpparses any pasted Authentication-Results header and surfaces the SPF, DKIM, DMARC, and ARC verdicts in human-readable form. - Does Microsoft 365 support SPF macros like %{i} or %{s}?
-
Microsoft 365 / Exchange Online publishes a
spf.protection.outlook.comrecord that does NOT use SPF macros — the record contains only literalip4andip6mechanisms. SPF macros (defined in RFC 7208 section 7) including%{i}(sending IP),%{s}(sender local- part),%{d}(sender domain),%{p}(validated domain name) are receiver-side evaluation features rather than sender-side authorisation features, and Microsoft as a sender publishes a macro-free record. Operators authoring their own SPF records for Exchange Online deployments should avoid SPF macros in the include chain — most third- party email reputation services and SPF-checking tools handle the basic macro set but compatibility is uneven and macros add operational complexity for limited benefit. The exception is the%{i}macro in theexistsmechanism for catch-all senders, which is a niche pattern used by some forensic-SPF deployments but not recommended for general Exchange Online use. - What is the difference between -all and ~all on a Microsoft 365 SPF record?
-
The
-allqualifier producesspf=failfor any sender whose IP is not on the authorised list — receivers including Gmail, Microsoft 365, Yahoo, and Apple treat-allthe same as a hardfail and apply DMARC enforcement (quarantine or reject) accordingly. The~allqualifier producesspf=softfail— receivers may accept the message with reduced trust, may quarantine, or may treat softfail equivalently to fail depending on the receivers spam- filter policy. Microsoft itself recommends-allin the canonical Microsoft Learn documentation for Exchange Online tenants because thespf.protection.outlook.comrecord covers every legitimate Exchange Online outbound IP — any other source is by definition not the tenants outbound mail and SHOULD be hardfailed. Tenants migrating from a legacy on-prem Exchange organisation or running a hybrid deployment may temporarily use~allduring migration to avoid hardfailing mail from unmigrated mail flows, but should graduate to-allas soon as all senders are inventoried and added to the SPF record. The Feb 2024 Google and Yahoo bulk-sender requirements ask for SPF, DKIM and DMARC and do not prescribe-allor~all.
Verify your Microsoft 365 SPF record now
DMARC Analyzer parses the daily aggregate
reports Microsoft, Google, Yahoo, and other
receivers send to your rua=
mailbox, deduplicates by
report_id + org_name,
and surfaces the per-sender / per-IP / per-day
breakdown needed to spot the
onmicrosoft.com alignment gotcha,
missing third-party-sender authorisations,
forwarding-induced SPF failures, and the
realised SPF lookup count. Live DNS
verification for every SPF, DKIM, and DMARC
record on your domain runs through
includes/DnsChecker.php; the
Email Header
Analyzer at
header_analyzer.php diagnoses
individual message failures by parsing the
full Authentication-Results stack via
includes/EmailHeaderParser.php.