Microsoft 365

SPF, DKIM and DMARC for Microsoft 365: set up, test and reach p=reject

Publish SPF, enable DKIM signing and roll DMARC out from p=none to p=reject for Microsoft 365 custom domains, with DNS examples, PowerShell commands and header checks.

12 min read
On this page

For a custom domain in Microsoft 365 you need three things: one SPF TXT record that includes spf.protection.outlook.com and ends in -all, DKIM signing turned on after you publish the two selector1 and selector2 CNAME records, and a DMARC TXT record at _dmarc that starts at p=none with aggregate reporting. Once the reports show that every legitimate sender passes with alignment, move the DMARC policy to p=quarantine and then p=reject, stepping up with pct and finishing with the parent domain.

Who this is for and what you will have at the end

This guide is for Microsoft 365 and Exchange Online administrators who own the DNS for one or more custom domains and want email from those domains to be trusted by receivers, and spoofed mail to be rejected. It also helps if bulk mail to Outlook.com is bouncing with 550 5.7.515.

At the end you will have:

  • A correct SPF record for each sending domain and subdomain, and a deny-all record for parked domains.
  • DKIM signing with your own domain, verified in message headers.
  • DMARC at p=reject for every domain, reached in stages without blocking legitimate mail.

If you are moving mail into Microsoft 365 at the same time, plan these records alongside the MX cutover described in the Google Workspace to Microsoft 365 migration guide.

How SPF, DKIM and DMARC work together

Each mechanism checks something different, and DMARC is the one that ties them to the address users actually see.

MechanismWhat it validatesDNS record in Microsoft 365
SPFThat the sending server is an authorized source for the domain in the MAIL FROM (envelope sender, 5321.MailFrom) addressOne TXT record per domain or subdomain
DKIMThat signed parts of the message weren't altered, and which domain signed it (the d= value)Two CNAME records per domain pointing to Microsoft-managed keys
DMARCThat SPF or DKIM passed and the authenticated domain aligns with the From (5322.From) domain; tells receivers what to do on failure and where to send reportsOne TXT record at _dmarc

A message passes DMARC if SPF or DKIM passes with alignment. It fails only when both fail. Alignment is relaxed by default, which means the organizational domains must match (bounces.contoso.com aligns with contoso.com); strict alignment (aspf=s, adkim=s) requires the exact same domain.

For the initial *.onmicrosoft.com domain, Microsoft already maintains SPF and signs outbound mail with DKIM. You still have to add a DMARC record for it yourself in the Microsoft 365 admin center.

There is also a deliverability reason to finish this work. Microsoft's consumer services (Outlook.com, Hotmail, Live.com) treat a domain that sends 5,000 or more messages to them as a high-volume sender. Those senders must pass SPF and DKIM, publish a DMARC record (at least p=none), and pass DMARC with SPF or DKIM aligned to the From domain, or messages are rejected with 550 5.7.515 Access denied, sending domain <domain> does not meet the required authentication level.

Prerequisites

  • The domain is added and verified in Microsoft 365. DKIM can't be configured for a domain until it has been added.
  • Access to the DNS hosting service for the domain. There are no admin portals or cmdlets in Microsoft 365 for managing SPF or DMARC records on custom domains; you create them at the DNS host.
  • Admin permissions to use the Email authentication settings page in the Microsoft Defender portal, or to run the DKIM cmdlets in Exchange Online PowerShell.
  • A mailbox (or Microsoft 365 group) dedicated to receiving DMARC aggregate reports, or a DMARC reporting service. The Microsoft Intelligent Security Association catalog lists reporting vendors.
  • An inventory of every system that sends mail as your domain: Microsoft 365, on-premises or hybrid Exchange servers, scanners and applications that relay mail, marketing platforms, ticketing systems, CRM and payroll services.

Step 1: Inventory senders and decide on subdomains

Write down each sending system, the From domain it uses, and how it can authenticate: an include: value or IP addresses for SPF, and whether it can DKIM-sign with your domain.

Microsoft recommends putting services you don't directly control, such as bulk email platforms, on a subdomain like marketing.contoso.com. That protects the reputation of the main domain and gives the subdomain its own budget of 10 SPF lookups. Remember that each subdomain that sends mail needs its own SPF record and its own DKIM configuration, while DMARC on the parent domain covers subdomains that don't have their own DMARC record.

Step 2: Publish the SPF record

The syntax is v=spf1 <valid mail sources> <enforcement rule>. For a domain where Microsoft 365 is the only sender:

Host: @ (contoso.com)
Type: TXT
Value: v=spf1 include:spf.protection.outlook.com -all

For GCC High and DoD the include is spf.protection.office365.us, and for Microsoft 365 operated by 21Vianet it is spf.protection.partner.outlook.cn.

When an on-premises server at 192.168.0.10 also sends for contoso.com, and a bulk mailer that documents include:servers.adatum.com sends for marketing.contoso.com, the records become:

contoso.com            TXT  v=spf1 ip4:192.168.0.10 include:spf.protection.outlook.com -all
marketing.contoso.com  TXT  v=spf1 include:servers.adatum.com include:spf.protection.outlook.com -all

Rules to follow:

  • Use -all. Microsoft recommends hard fail for Microsoft 365 domains because DMARC decides what happens to failures. DMARC treats ~all and -all both as SPF failures, but for ~all failures the DMARC policy is effectively ignored if the message also lacks a DKIM signature.
  • One record per domain. Two SPF records on the same name cause a permerror.
  • Fewer than 10 DNS lookups. include, a, mx, exists and redirect each count, and nested includes count too. ip4, ip6 and all don't. Exceeding the limit makes SPF fail with a permanent error.
  • TTL of at least 3600 seconds to avoid lookup timeouts.
  • No syntax slips. A trailing period after a domain, include= instead of include:, or a space after the colon all break validation.
  • Don't flatten Microsoft 365. Microsoft advises against replacing include:spf.protection.outlook.com with IP addresses, because its sending infrastructure uses IP addresses that change frequently.

For parked domains that never send mail, publish v=spf1 -all.

Step 3: Turn on DKIM signing

Microsoft 365 generates two key pairs per domain. Only one selector is active at a time; the other is used after a key rotation. Your DNS hosts two CNAME records that point to Microsoft-managed public keys.

Get the exact CNAME values

Don't construct the values by hand. New custom domains use a format that includes a dynamically assigned partition character, for example selector1-contoso-com._domainkey.contoso.n-v1.dkim.mail.microsoft, while older domains use selector1-contoso-com._domainkey.contoso.onmicrosoft.com. Read them from Exchange Online PowerShell:

Connect-ExchangeOnline -UserPrincipalName admin@contoso.com
Get-DkimSigningConfig | Format-List Name,Enabled,Status,Selector1CNAME,Selector2CNAME

If the domain isn't listed, create the configuration first, then run the previous command again:

New-DkimSigningConfig -DomainName contoso.com -Enabled $false

New-DkimSigningConfig defaults to a 1024-bit key; add -KeySize 2048 if you want 2048-bit keys from the start.

In the Defender portal, the same values appear under Email & collaboration > Policies & rules > Threat policies > Email authentication settings > DKIM tab. Open the domain's details flyout and copy the values in the Publish CNAMEs section.

Create the CNAME records

Host: selector1._domainkey   Type: CNAME   Points to: <Selector1CNAME value>   TTL: 3600
Host: selector2._domainkey   Type: CNAME   Points to: <Selector2CNAME value>   TTL: 3600

Enter only selector1._domainkey in the host field; most DNS providers append the zone automatically. Create both records even though only one selector is active, because the second one is needed for key rotation.

Enable signing

After Microsoft 365 detects the records (a few minutes or longer), enable signing in the domain's flyout with Sign messages for this domain with DKIM signatures, or run:

Set-DkimSigningConfig -Identity contoso.com -Enabled $true
Get-DkimSigningConfig -Identity contoso.com | Format-List Name,Enabled,Status

The domain should show Enabled: True and Status: Valid. If the records aren't detected yet, the command returns an error containing the expected CNAME values; check for typos and try again later.

To rotate keys later, run Rotate-DkimSigningConfig -Identity contoso.com, optionally with -KeySize 2048. A rotation takes four days (96 hours) before the new key starts signing, and you can't start another rotation while one is in progress.

Step 4: Publish DMARC in monitoring mode

The DMARC record lives at the _dmarc host name:

Host: _dmarc
Type: TXT
Value: v=DMARC1; p=none; pct=100; rua=mailto:dmarc-reports@contoso.com
  • p= is the policy for failing messages: none, quarantine or reject.
  • pct= is the percentage of failing messages the policy applies to. It defaults to 100.
  • rua=mailto: receives aggregate reports, usually sent once a day by each receiving system as a compressed XML attachment listing source IP addresses, message counts and SPF/DKIM alignment results.
  • ruf=mailto: requests forensic (failure) reports. Each receiving system decides whether to send them, and Microsoft 365 itself doesn't send forensic reports, so don't build your process around them.

For the *.onmicrosoft.com domain, add the record in the Microsoft 365 admin center under Settings > Domains, select the domain, open the DNS records tab and add a TXT record named _dmarc. Microsoft's example value is v=DMARC1; p=reject, which is appropriate if you don't send mail from that domain.

Step 5: Test before you tighten

Check what DNS returns

From Windows PowerShell, query the records the way a receiver would:

Resolve-DnsName -Name contoso.com -Type TXT
Resolve-DnsName -Name selector1._domainkey.contoso.com -Type CNAME
Resolve-DnsName -Name _dmarc.contoso.com -Type TXT

Confirm there is exactly one v=spf1 string, both selectors resolve as CNAME records (not TXT), and the DMARC record starts with v=DMARC1.

Read the message headers

Send a message from a mailbox in the domain to an external mailbox, such as an Outlook.com or Gmail address. DKIM signatures aren't added when the sender and recipient are in the same organization, so internal tests show dkim=none. Microsoft also advises against using AOL for this test because it might skip the DKIM check when SPF passes.

In the received message, open the headers (in Outlook, or paste them into the Message Header Analyzer at mha.azurewebsites.net) and look for fields like these (values shortened):

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=contoso.com; s=selector1; ...
Authentication-Results: ...; dkim=pass header.d=contoso.com; spf=pass smtp.mailfrom=contoso.com; dmarc=pass ...

The d= value must be your domain, not contoso.onmicrosoft.com, for DKIM to align.

Read the aggregate reports

Give the reports enough time to capture infrequent senders such as monthly jobs, and remember that report volume rises and falls with your own mail volume. For each source in the reports:

  • Microsoft 365 IP addresses with aligned SPF and DKIM: healthy.
  • A known service where SPF passes but alignment fails: it uses its own MAIL FROM domain. Configure it to sign with DKIM as your domain, or use your domain in MAIL FROM.
  • A known service where DKIM passes for d=adatum.com but DMARC fails: it signs with its own domain. Configure custom DKIM at the service.
  • Unknown IP addresses with high volume and failing everything: spoofing that DMARC will block once enforced.
  • Mailing lists and forwarders failing everything: expected, because forwarding breaks SPF and body changes break DKIM.

Step 6: Move to quarantine, then reject

Start with a low-volume subdomain and repeat for each domain, leaving the parent domain for last:

v=DMARC1; p=quarantine; pct=10;  rua=mailto:dmarc-reports@contoso.com
v=DMARC1; p=quarantine; pct=25;  rua=mailto:dmarc-reports@contoso.com
v=DMARC1; p=quarantine; pct=50;  rua=mailto:dmarc-reports@contoso.com
v=DMARC1; p=quarantine; pct=100; rua=mailto:dmarc-reports@contoso.com
v=DMARC1; p=reject;     pct=100; rua=mailto:dmarc-reports@contoso.com

Hold each stage long enough to review a full reporting cycle and fix every legitimate failure before moving on. Microsoft notes that outbound mail from Microsoft 365 domains with p=quarantine or p=reject that fails DMARC at the destination is routed through the high-risk delivery pool, so fix failing internal senders rather than living with them.

For parked domains, skip the stages and publish v=DMARC1; p=reject; alongside v=spf1 -all. Don't publish DKIM records for parked domains.

Troubleshooting

SymptomLikely causeFix
SPF permerrorTwo SPF records on one name, or more than 10 lookupsMerge into one record; move services to subdomains or replace stable vendor includes with IP ranges.
SPF softfail or fail for a legitimate systemIts IP address or include is missingAdd the documented include or IP range for that sender.
DKIM status stays CnameMissingHost entered as selector1._domainkey.contoso.com and the zone was appended twice, a TXT record created instead of a CNAME, or a typo in the targetUse only selector1._domainkey as the host, delete TXT records, copy values from Get-DkimSigningConfig. The DKIM CnameMissing fix covers each case.
dkim=fail (body hash did not verify)A gateway, mailing list or rule changed the body after signingFor mail arriving in Microsoft 365 through a gateway that modifies it, configure the gateway as a trusted ARC sealer (see ARC trusted sealers and Enhanced Filtering).
DKIM and SPF pass, DMARC failsNeither smtp.mailfrom nor header.d aligns with the From domainConfigure the service to sign with your domain or use your domain in MAIL FROM.
550 5.7.515 from Outlook.comHigh-volume sender failing the authentication requirementsPass SPF and DKIM, publish DMARC, and make sure one of them aligns.
550 5.7.23 from a receiverSPF for the sending domain is missing or misconfiguredFix the SPF record.

Checklist

  • Inventory every sending system and move uncontrolled senders to subdomains.
  • One SPF TXT record per sending domain and subdomain, ending in -all, under 10 lookups, TTL 3600.
  • Two DKIM CNAME records per domain, values copied from Get-DkimSigningConfig, signing enabled and Status: Valid.
  • DMARC at p=none with rua reporting, including the *.onmicrosoft.com domain.
  • External test messages show spf=pass, dkim=pass with your d= domain, and dmarc=pass.
  • Aggregate reports reviewed and every legitimate source aligned.
  • p=quarantine with increasing pct, then p=reject, subdomains first and the parent last.
  • Parked domains: v=spf1 -all and v=DMARC1; p=reject;, no DKIM.

References

Questions people ask

What SPF record does a Microsoft 365 domain need?

If Microsoft 365 is the only source of mail for the domain, publish one TXT record with the value v=spf1 include:spf.protection.outlook.com -all. Add ip4, ip6 or include values for every other system that sends as the domain, keep it to a single SPF record per domain or subdomain, and stay under 10 DNS lookups.

Is DKIM turned on automatically for custom domains in Microsoft 365?

No. Microsoft 365 signs outbound mail from the initial onmicrosoft.com domain automatically, but for a custom domain you must create two CNAME records (selector1._domainkey and selector2._domainkey) and then enable signing in the Defender portal or with Set-DkimSigningConfig. Mail signed only with the onmicrosoft.com domain doesn't align with your From domain for DMARC.

Should I start DMARC at p=reject?

Not for a domain that already sends mail. Microsoft recommends starting at p=none with aggregate reporting, moving to p=quarantine, and then to p=reject, using the pct value to step up gradually and doing low-volume subdomains before the parent domain. Parked domains that never send mail can go straight to p=reject.

What does the 550 5.7.515 bounce mean?

Outlook.com and other Microsoft consumer services reject mail from domains that send 5,000 or more messages to them when the domain doesn't meet the authentication requirements. SPF and DKIM must pass, a DMARC record must exist (p=none is enough), and DMARC must pass with SPF or DKIM aligned to the From domain.

Exchange OnlineSPFDKIMDMARCDNS
  1. Fix DKIM CnameMissing in Microsoft 365 and enable signing for your domain

    Why Microsoft 365 reports CnameMissing when you enable DKIM, how to publish the exact selector CNAME records, and how to confirm the domain signs mail.

    Microsoft 36510 min read
  2. Email migration cutover checklist: DNS TTLs, MX switch and day one

    A practical cutover runbook for moving a domain's mail to Exchange Online: lower TTLs early, switch MX and Autodiscover safely, and support users on day one.

    Microsoft 36510 min read
  3. Outlook Autodiscover problems with Microsoft 365: DNS checks and fixes

    Fix Outlook profiles that can't find an Exchange Online mailbox: check the Autodiscover CNAME, root domain responses, cached URLs, registry and Group Policy settings.

    Microsoft 36511 min read