Email authentication
Microsoft 365 DMARC setup
By Adam W., founder of DMARCit · Last updated 2026-06
Microsoft 365 signs outbound mail with DKIM automatically, but only for your tenant's *.onmicrosoft.com address. The custom domain you actually send from leaves Exchange Online unsigned until you publish two CNAME records and flip a switch. That is the gap most M365 tenants ship into production with, and it is the gap that breaks DMARC alignment under DMARC the moment a receiving server checks more carefully than Microsoft used to.
What M365 gives you out of the box
When you provision a new Microsoft 365 tenant, every tenant gets a default routing domain at contoso.onmicrosoft.com (substitute your tenant name). Exchange Online automatically DKIM-signs outbound mail whose From: domain is that.onmicrosoft.com address. Microsoft manages the key, rotates it, and publishes the public selector for you.
Almost no tenant actually sends mail with the .onmicrosoft.com address in the visible From: header. Your users send as finance@contoso.com, not finance@contoso.onmicrosoft.com. The default DKIM signing does not cover that mail. Exchange Online still puts a DKIM signature on it (using the tenant .onmicrosoft.com domain in d=), but for DMARC purposes that signature does not align with the visible From: domain. DMARC fails on the DKIM side until you set up a signing configuration for your custom domain.
SPF is the other half. Microsoft publishes its own SPF record atspf.protection.outlook.com, and your tenant inherits a Return-Path under that domain for outbound Exchange Online mail. SPF passes by default, and because the Return-Path is under a Microsoft domain rather than yours, it does not align with your From: domain either. That is fine for inbound mail flow, but it means SPF on its own cannot satisfy DMARC alignment for outbound M365 mail. DKIM is the only path. Hence the importance of getting custom DKIM live before you publish anything stricter than p=none.
Custom-domain DKIM setup
Microsoft uses a two-selector model so you can rotate keys without downtime. You publish two CNAMEs at selector1._domainkey andselector2._domainkey under your custom domain. Both CNAMEs point at Microsoft-managed targets that include your tenant's initial domain and a key identifier.
The published records look like this:
selector1._domainkey.contoso.com CNAME selector1-contoso-com._domainkey.contoso.onmicrosoft.com selector2._domainkey.contoso.com CNAME selector2-contoso-com._domainkey.contoso.onmicrosoft.com
The exact left-hand and right-hand labels vary by tenant. Do not copy these verbatim. Use Get-DkimSigningConfig (covered below) to retrieve the correct targets for your tenant, or read them from the Microsoft Defender portal under Email & collaboration → Policies & rules → Threat policies → Email authentication settings → DKIM.
Once both CNAMEs resolve in DNS, enable signing for the domain. The portal calls this “Sign messages for this domain with DKIM signatures.” PowerShell calls it Enabled = $true on the signing configuration. Until both CNAMEs are live, the enable step fails with an error along the lines of “No DKIM keys saved for this domain.”
Microsoft holds the private keys. You never see them, and you never rotate them manually. The two-selector design means Microsoft can roll a new key into one selector while the other stays live for verification of in-flight mail.
PowerShell commands you actually use
Connect to Exchange Online with the V3 module and the modern auth flow:
Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser Connect-ExchangeOnline -UserPrincipalName admin@contoso.com
Read the current DKIM signing state for every domain:
Get-DkimSigningConfig | Format-List Domain, Enabled, Status, Selector1CNAME, Selector2CNAME, KeyCreationTime, LastChecked
For a specific domain:
Get-DkimSigningConfig -Identity contoso.com | Format-List
The Selector1CNAME and Selector2CNAME fields are the exact CNAME targets you publish in DNS. Status tells you whether the CNAMEs are resolving the way Microsoft expects.
Enable signing for a custom domain:
Set-DkimSigningConfig -Identity contoso.com -Enabled $true
Create a signing config for a domain that does not have one yet:
New-DkimSigningConfig -DomainName contoso.com -Enabled $false
Creating with -Enabled $false lets you collect the CNAME targets, publish them in DNS, wait for propagation, then flip enabled to true. Skipping the propagation wait is the most common reason the enable step fails the first time.
Rotate the active key:
Rotate-DkimSigningConfig -Identity contoso.com -KeySize 2048
Use 2048 unless you have a specific reason not to. The 1024-bit option exists for legacy interoperability. RFC 8301 treats 2048 as the floor for new deployments.
The SPF record for Exchange Online
The baseline SPF record for an Exchange-Online-only tenant is:
v=spf1 include:spf.protection.outlook.com -all
The Microsoft include resolves to between two and five DNS-querying mechanisms at any given moment, depending on how Microsoft has structured its IP ranges that week. Plan for the higher end of that range when you start adding third-party senders, because the RFC 7208 ten-lookup ceiling is enforced as a hard limit and an overage returns permerror for every message.
The -all at the end says receivers should treat everything not authorized by the record as a hard fail. Microsoft documents -all as the recommended posture for production sending domains. The softer ~all is sometimes useful during cutover but you should ratchet to -all once you trust the record.
Two SPF mistakes I see repeatedly in M365 environments. First, addinginclude: entries for every SaaS tool a department signs up for, without checking whether the tool actually uses your domain in the Return-Path. If the tool bounces under bounces.sendgrid.net rather thanbounces.contoso.com, your SPF include for that tool does nothing for DMARC alignment and just eats lookups. Second, adding a or mx mechanisms because someone read a tutorial from 2015. Outbound M365 mail does not leave through your MX hosts, so those mechanisms authorize nothing useful and each one costs a lookup.
→ If you have already breached the limit: SPF PermError: Too many DNS lookups. For a deeper look at SPF mechanics: What is SPF?
Publishing your DMARC record
DMARC is a TXT record at _dmarc.contoso.com. The order of operations for an M365 tenant matters. Get DKIM signing live first. Verify SPF resolves cleanly. Then publish DMARC at p=none with reporting:
_dmarc.contoso.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@contoso.com; ruf=mailto:dmarc@contoso.com; fo=1; adkim=r; aspf=r"
Two tags worth being deliberate about. adkim=r and aspf=r are the default (relaxed alignment) and the right choice for M365 because your tenant frequently uses subdomain bounce addresses and DKIM domains. Setting strict alignment will break legitimate Microsoft mail flow until you have a tightly managed sender inventory.
The reporting address (rua=) is the thing that makes the policy useful. Without aggregate reports flowing to a tool that parses them, you are publishing DMARC blind. Receiving providers send those reports daily, typically as XML attachments. M365 itself does not provide a built-in DMARC report viewer, so this is where a parser earns its keep.
Once aggregate reports identify every legitimate sender and confirm DKIM is aligning, move to p=quarantine with a pct= ramp, then top=reject. Detailed rollout mechanics: DMARC pct rollout.
MOERA and tenant-routed mail
MOERA (Mail Originated by Email Address) is the term Microsoft uses for mail flow where the tenant routes mail using an email address rather than a host-based routing decision. In practice this matters when you have multiple accepted domains on a single tenant, or when you use connectors that route based on the From: address.
The thing that catches people out: every accepted domain on the tenant needs its own DKIM signing configuration. Adding a domain to the tenant does not give it DKIM signing. The default state is Enabled = $false. Until you create and enable a config, outbound mail from that domain is signed with the tenant .onmicrosoft.com domain in d=, which fails DMARC alignment for the new domain.
The fix is the same as for the primary domain. Publish selector CNAMEs, create the signing config with New-DkimSigningConfig, and enable it after propagation. Cycle through every accepted domain on the tenant. The portal lists them; PowerShell does too:
Get-AcceptedDomain | Select Name, DomainName, DomainType
Parked and non-sending domains
Domains that never send legitimate mail still need DMARC. Without a record, a spoofer can use the domain in the From: header of phishing mail and nothing on the receiving side has a basis to reject it. With a strict no-send DMARC record, the spoofed mail dies at the receiver.
The non-sending baseline record:
v=spf1 -all
v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc@yourprimary.com"
SPF says nothing is authorized. DMARC rejects on the policy side. Send aggregate reports to a mailbox on your primary domain so you can see if a spoofer is actively trying. M365 tenants often hold five or ten domains acquired through brand purchases, M&A, or typo-defense registrations. Every one of them is a free spoofing surface until you cover it with a parked-domain record.
Exchange Online forwarding and SRS
When a mailbox forwards to an external address, Exchange Online rewrites the envelope sender using Sender Rewriting Scheme (SRS). The Return-Path on the forwarded copy becomes something likeBN0PR01CU001-mail@contoso.onmicrosoft.com rather than the original sender. This lets SPF pass at the next hop because the forwarded mail is now authorized by the Microsoft SPF record.
The DMARC implication: SPF on the forwarded mail no longer aligns with the original From: domain. Alignment now depends entirely on DKIM surviving the forward. If the original sender DKIM-signed the mail with their own domain, and Exchange Online did not modify the body, the original DKIM signature is still valid and DMARC passes. If the original sender did not sign with DKIM, or the forward involves any body modification, DMARC fails on the forwarded copy.
This is one reason ARC (Authenticated Received Chain) matters. Major receivers including Microsoft and Google validate ARC and use it as a tie-breaker on forwarded mail. M365 adds ARC headers on forwarded mail, which gives downstream receivers a basis to trust authentication results that would otherwise fail.
Why third-party senders still fail
The single most common DMARC failure mode in M365 tenants is third-party mail that uses the vendor's own DKIM signing domain rather than yours. SendGrid, Mailchimp, HubSpot, Zendesk, Salesforce Marketing Cloud, and most billing platforms ship a default setup where they DKIM-sign with their own domain. The signature verifies cleanly. DMARC fails because d=sendgrid.net does not align with From: noreply@contoso.com.
Every one of these vendors offers a custom DKIM setup. The setup involves publishing one or more CNAMEs in your DNS that delegate signing to the vendor. After the vendor enables custom signing on their side, d= on outbound mail becomes your domain (or a subdomain of it under relaxed alignment) and DMARC starts passing.
A handful of failure patterns I see repeatedly:
- Vendor onboarding wizards that say “DKIM verified” without ever requiring a CNAME. That usually means they verified that the message validates under their domain, not yours. DMARC will still fail.
- Marketing automation tools that route bounces to their own domain. SPF passes for their domain (the Return-Path domain), but does not align with your From:. DKIM alignment is the only path; SPF is doing nothing for you.
- Internal app servers that send through SMTP relay using a connector but with a From: address on your domain. These usually need an inbound connector with the right header preservation, plus DKIM signing handled by Exchange Online on the outbound side.
- One-off vendors that disappear from the SaaS stack but linger in your SPF record. Their
include:burns a DNS lookup forever until someone audits.
See: DMARC alignment failure.
550 5.7.515 bulk-sender enforcement
Microsoft began rejecting unauthenticated mail from high-volume senders to Outlook.com consumer mailboxes in May 2025. The rejection arrives as SMTP550 5.7.515 with text along the lines of “Access denied, sending domain [contoso.com] does not pass DMARC verification.” Microsoft defines high-volume as 5,000 messages per day or more to Outlook consumer mailboxes, but in practice the threshold is enforced at the domain level rather than the message level.
The fix is the standard fix. Publish DKIM that aligns with your From: domain. Publish SPF that resolves cleanly under the ten-lookup limit. Publish DMARC at least at p=none with a reporting address. Microsoft enforces against the presence of a DMARC record and against the authentication signal aligning, not against the policy strength.
The trap to avoid: publishing DMARC p=none with no reporting and calling it done. That satisfies the surface requirement but leaves you blind to the ongoing failures. The whole point of p=none is to gather data that lets you move to enforcement, and gathering data requires actually receiving and parsing the reports.
Detailed remediation steps: 550 5.7.515: Outlook authentication rejection.
Hybrid M365 plus on-prem Exchange
In a hybrid deployment, mail can leave through Exchange Online or through your on-prem Exchange server, depending on mailbox location and connector routing. DKIM signing happens on the egress side, so a mailbox hosted on-prem that routes outbound through the on-prem server gets no Exchange Online DKIM signing at all.
On-prem Exchange (2016, 2019, Subscription Edition) does not include native DKIM signing. You need a transport agent. For pure hybrid environments the cleanest answer is usually to route all outbound through Exchange Online via a centralized mail flow connector, which lets the cloud handle DKIM signing for every mailbox regardless of where the mailbox lives.
For the deeper on-prem mechanics, including the transport agent options and connector configurations: Microsoft Exchange DMARC.
Rolling out to p=reject
The path from a freshly published p=none record to p=reject for an M365 tenant usually takes between two weeks and three months, depending on the size of the SaaS sender stack. The sequence:
- Publish DMARC at
p=nonewithrua=pointing to a parser. Wait at least 14 days. Longer for tenants with infrequent senders. - From the aggregate reports, build a complete list of every IP and domain sending as you. Cross-reference against your known SaaS stack. The unknowns are either forgotten vendors or spoofers, and both are interesting.
- For every legitimate sender that fails DMARC, work through the vendor's custom DKIM setup. Re-check aggregate reports after each vendor change. Repeat until known senders are at 100 percent alignment.
- Move to
p=quarantine; pct=10. Watch reports for legitimate mail landing in spam. Ramp topct=25,pct=50,pct=100. - Move to
p=reject; pct=10and ramp the same way. The percentage ramp at the reject stage is mostly belt-and-suspenders by this point but the cost is low and the safety margin is real. - Set
sp=rejectfor subdomains and publish parked-domain records on every domain in the tenant that does not send. - Keep monitoring. New senders show up. Vendor IPs rotate. DKIM keys get accidentally removed. The reports are the only signal.
See your M365 senders in one place
DMARCit parses your aggregate reports and shows every service sending as your custom domains, with DKIM and SPF alignment status. Add your domain, pointrua= at DMARCit, and the first sender list usually appears within 24 hours.