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=rejectfor 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.
| Mechanism | What it validates | DNS record in Microsoft 365 |
|---|---|---|
| SPF | That the sending server is an authorized source for the domain in the MAIL FROM (envelope sender, 5321.MailFrom) address | One TXT record per domain or subdomain |
| DKIM | That 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 |
| DMARC | That 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 reports | One 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 -allFor 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 -allRules to follow:
- Use
-all. Microsoft recommends hard fail for Microsoft 365 domains because DMARC decides what happens to failures. DMARC treats~alland-allboth as SPF failures, but for~allfailures 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,existsandredirecteach count, and nested includes count too.ip4,ip6andalldon'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 ofinclude:, or a space after the colon all break validation. - Don't flatten Microsoft 365. Microsoft advises against replacing
include:spf.protection.outlook.comwith 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,Selector2CNAMEIf the domain isn't listed, create the configuration first, then run the previous command again:
New-DkimSigningConfig -DomainName contoso.com -Enabled $falseNew-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: 3600Enter 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,StatusThe 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.comp=is the policy for failing messages:none,quarantineorreject.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 TXTConfirm 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.combut 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.comHold 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
| Symptom | Likely cause | Fix |
|---|---|---|
SPF permerror | Two SPF records on one name, or more than 10 lookups | Merge into one record; move services to subdomains or replace stable vendor includes with IP ranges. |
SPF softfail or fail for a legitimate system | Its IP address or include is missing | Add the documented include or IP range for that sender. |
DKIM status stays CnameMissing | Host 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 target | Use 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 signing | For 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 fails | Neither smtp.mailfrom nor header.d aligns with the From domain | Configure the service to sign with your domain or use your domain in MAIL FROM. |
550 5.7.515 from Outlook.com | High-volume sender failing the authentication requirements | Pass SPF and DKIM, publish DMARC, and make sure one of them aligns. |
550 5.7.23 from a receiver | SPF for the sending domain is missing or misconfigured | Fix 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 andStatus: Valid. - DMARC at
p=nonewithruareporting, including the*.onmicrosoft.comdomain. - External test messages show
spf=pass,dkim=passwith yourd=domain, anddmarc=pass. - Aggregate reports reviewed and every legitimate source aligned.
p=quarantinewith increasingpct, thenp=reject, subdomains first and the parent last.- Parked domains:
v=spf1 -allandv=DMARC1; p=reject;, no DKIM.
References
- Set up SPF to identify valid email sources for your custom cloud domains
- Set up DKIM to sign mail from your cloud domain
- Set up DMARC to validate the From address domain for cloud senders
- Troubleshoot email authentication in Microsoft 365
- Email authentication in Microsoft 365
- Fix NDR error "550 5.7.515" in Outlook.com
- Resolve-DnsName
- Connect to Exchange Online PowerShell