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 -all published 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.com mechanism delegates SPF evaluation to a Microsoft-managed TXT record at spf.protection.outlook.com, which lists the ip4 and ip6 ranges 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 alongside include:spf.protection.outlook.com. Centralized Mail Transport (CMT) topology routes through on-prem; Direct-Delivery uses both paths.
  • The <tenant>.onmicrosoft.com SPF trap: Exchange Online routes outbound mail with the tenant's onmicrosoft.com MAIL 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, not onmicrosoft.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.com consumes 1 lookup (the include itself; the inner record is flat ip4 / ip6); third-party includes cost between 1 and 5 each. Verify with dig +short or 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 at security.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.com ranges should show spf=pass; any unfamiliar IP claiming MAIL FROM=<your-domain> with spf=fail is 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 central spf.protection.outlook.com TXT 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 of spf.protection.outlook.com) receives spf=fail. Receivers including Gmail, Yahoo, Microsoft 365 itself, and Apple Mail apply DMARC enforcement (quarantine or reject) uniformly on fail verdicts.

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. 1 lookup for the include:spf.protection.outlook.com mechanism itself (per RFC 7208 section 4.6.4 each include is one DNS lookup).
  2. 0 additional lookups inside the include — the spf.protection.outlook.com record contains only ip4 and ip6 mechanisms 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 include mechanism (1 lookup each).
  • Each a mechanism (1 lookup each).
  • Each mx mechanism (1 lookup each).
  • Each ptr mechanism (1 lookup each, plus the reverse lookup — most operators avoid ptr as it is deprecated by RFC 7208 itself).
  • Each exists mechanism (1 lookup each).
  • Each redirect modifier (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:

  1. Run a recursive SPF resolver tool. 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.
  2. Or run dig +short txt example.com and then recursively dig +short txt <each-included-domain> — counting every include, a, mx, ptr, exists, and redirect encountered.
  3. 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 the TXT records 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.com publishes v=spf1 include:servers.mcsv.net include:mktomail.com -all for marketing-mail-only senders and transactional.example.com publishes v=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=fail because 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:

  1. The default routing domain assignment. Newly created mailboxes are assigned <user>@<tenant>.onmicrosoft.com as the primary email address until the custom domain is set as primary.
  2. Service accounts and shared mailboxes. These often remain on onmicrosoft.com because they were created before the custom domain was added or because no operator explicitly switched them.
  3. App-only authentication. Applications using OAuth or SMTP AUTH with service-account credentials may send AS the service accounts onmicrosoft.com address 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.com ranges (Microsoft outbound IPs).
  • SPF result pass against <tenant>.onmicrosoft.com.
  • SPF-aligned result fail against the published custom domain.
  • DKIM result pass against 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:

  1. Navigate to Email & collaboration > Exchange message trace.
  2. Search for the test message by sender, recipient, or subject.
  3. Click the message to open the detailed view.
  4. 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 -all published as a TXT record at the apex of the sending domain. The include:spf.protection.outlook.com mechanism delegates SPF evaluation to the Microsoft-managed spf.protection.outlook.com record, which currently expands to roughly six ip4 ranges and one ip6 range 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 -all qualifier hardfails any sender whose IP is not on the Microsoft list. Use -all rather than ~all (softfail) so receivers including Gmail apply DMARC enforcement uniformly; Microsoft itself recommends -all in the canonical Microsoft Learn documentation. Operators sending via third-party services (Mailchimp, SendGrid, Salesforce) must add those services BEFORE the include:spf.protection.outlook.com mechanism, 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 ip4 mechanism 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. The include:spf.protection.outlook.com mechanism delegates SPF evaluation to a Microsoft-managed TXT record at spf.protection.outlook.com that Microsoft updates centrally. Tenants ride the central record by publishing a single include mechanism. The trade-off is that the spf.protection.outlook.com record 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.com mechanism, separated by a single space. Example: v=spf1 include:servers.mcsv.net include:sendgrid.net include:spf.protection.outlook.com -all adds 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 via dig +short or via the Domain Security Checker domain_checker.php tool BEFORE deploying to verify you remain under the 10-lookup cap. If the count exceeds 10 receivers including Gmail and Microsoft return spf=permerror and DMARC alignment fails.

What is the spf.protection.outlook.com lookup count?

The spf.protection.outlook.com record 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 publishes include:spf.protection.outlook.com — that is 1 lookup. Microsoft resolves the spf.protection.outlook.com TXT record — that is the cost of the include itself (counted). The spf.protection.outlook.com record begins with v=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 only ip4 / ip6 / -all mechanisms 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 records spfa.protection.outlook.com and spfb.protection.outlook.com and cost more than one lookup; today it is flat. Operators should still run the live count via dig +short or domain_checker.php to verify current behaviour.

What is the onmicrosoft.com SPF problem?

Every Microsoft 365 tenant is assigned a default <tenant>.onmicrosoft.com routing 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 Microsofts onmicrosoft.com record 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, and onmicrosoft.com does NOT align with example.com — DMARC fails with spf-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 the onmicrosoft.com address by default require explicit reassignment via Set-Mailbox -EmailAddresses or 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 -all where <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 the include:spf.protection.outlook.com is 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 BOTH ip4:<onprem> and include:spf.protection.outlook.com are 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 with v=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 current ip4 / ip6 ranges. (3) Microsoft 365 message trace via the Defender portal at security.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 show spf=pass smtp.mailfrom=<your-domain>. (4) PowerShell from a workstation with the ExchangeOnlineManagement module installed: Connect-ExchangeOnline; Get-AcceptedDomain — confirms the domain is registered to the tenant. The Domain Security Checker domain_checker.php tool runs the same DNS lookups through includes/DnsChecker.php and surfaces the live realised lookup count, helping you spot the 10-lookup cap risk early. The Email Header Analyzer header_analyzer.php parses 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.com record that does NOT use SPF macros — the record contains only literal ip4 and ip6 mechanisms. 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 the exists mechanism 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 -all qualifier produces spf=fail for any sender whose IP is not on the authorised list — receivers including Gmail, Microsoft 365, Yahoo, and Apple treat -all the same as a hardfail and apply DMARC enforcement (quarantine or reject) accordingly. The ~all qualifier produces spf=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 -all in the canonical Microsoft Learn documentation for Exchange Online tenants because the spf.protection.outlook.com record 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 ~all during migration to avoid hardfailing mail from unmigrated mail flows, but should graduate to -all as 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 -all or ~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.

Sign up — Free plan Check my Microsoft 365 SPF setup