Enabling DKIM signing for your domain, publishing a DMARC policy, handling third-party senders, and moving from monitoring to enforcement without breaking legitimate mail.
SPF alone is not enough. Without DKIM and DMARC, a recipient mail system has no cryptographic proof that a message claiming to be from your domain actually came from you, and no instruction for what to do if it did not. This guide covers enabling both in Exchange Online, in the right order, without disrupting mail in the process.
SPF authorises IP addresses to send as your domain. DKIM adds a cryptographic signature to outbound messages that the recipient can verify. DMARC ties them together: it publishes a policy telling receivers what to do with messages that fail both checks, and where to send reports. A message passes DMARC if it passes either SPF or DKIM and the domain in that result aligns with the From address. DMARC without DKIM is fragile because SPF alignment breaks on forwarded mail; DKIM survives forwarding.
The order matters: enable DKIM before publishing any DMARC enforcement policy. A DMARC policy of p=reject with no DKIM in place will quarantine or reject legitimate mail from Exchange Online the moment a forwarding path breaks SPF alignment.
Exchange Online can sign outbound messages with DKIM for any verified custom domain. The process is: generate the signing keys, publish two CNAME records in DNS, then enable signing.
Exchange Online generates the CNAME records you need to publish. Retrieve them with:
Get-DkimSigningConfig -Identity contoso.com | Select-Object Domain, Selector1CNAME, Selector2CNAME, Status, Enabled
If the domain has never had DKIM configured, the status will show as CnameMissing or similar. The two selectors (selector1 and selector2) are used in rotation; Exchange Online handles key rotation automatically once both are published and signing is enabled.
In your DNS provider, create two CNAME records using the values from the command above. They follow this pattern:
| Host / Name | Type | Value |
|---|---|---|
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 values will differ per domain; use what Get-DkimSigningConfig returned, not the pattern above verbatim. DNS propagation typically takes a few minutes to an hour, but allow up to 48 hours before assuming something is wrong.
Once DNS has propagated, enable signing:
Set-DkimSigningConfig -Identity contoso.com -Enabled $true
Confirm the result:
Get-DkimSigningConfig -Identity contoso.com | Select-Object Domain, Enabled, Status, Selector1KeySize
Status should show Valid and Enabled as True. If it still shows CnameMissing, DNS has not propagated yet or the CNAME values were entered incorrectly. Check the raw DNS with:
Resolve-DnsName selector1._domainkey.contoso.com -Type CNAME
Before publishing DMARC, list every service that sends email as your domain: marketing platforms, CRMs, support ticketing systems, invoicing tools, e-signature services, automated alert senders. Every one of them needs to either be included in your SPF record or configured to DKIM-sign with your domain. A DMARC enforcement policy will reject mail from any sender that fails both.
p=quarantine or p=reject before auditing third-party senders. The result is legitimate transactional mail silently disappearing into quarantine at recipients. Use the monitoring phase (below) to find them before enforcing.For each third-party sender, you have two options: add their sending IPs to your SPF record (keeping the 10-lookup limit in mind), or configure that platform to sign with DKIM using your domain, which is what most modern platforms support. Paste your SPF record into the email auth analyser to check the lookup count before and after changes.
DMARC is a DNS TXT record published at _dmarc.contoso.com. Start with a monitoring-only policy that requests reports but takes no action on failing mail:
Monitoring policy (safe starting point)
_dmarc.contoso.com TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@contoso.com; ruf=mailto:dmarc-reports@contoso.com; fo=1"
The tags:
| Tag | Meaning |
|---|---|
p=none | Monitor only. No action taken on failing mail. Change this last, once you have reviewed reports. |
rua | Aggregate report destination. Daily XML summaries of pass/fail results from receivers. The most useful signal during the monitoring phase. |
ruf | Forensic (failure) report destination. Per-message reports for failures. Not all receivers send these. |
fo=1 | Generate a failure report if either SPF or DKIM fails (not only if both fail). |
sp | Subdomain policy. If omitted, the parent p= applies. Set sp=none to exempt subdomains during rollout. |
pct | Percentage of failing mail to apply the policy to. Useful for gradual enforcement rollout. Default is 100 if omitted. |
Leave the p=none policy in place for at least two to four weeks before moving toward enforcement. The aggregate reports arriving at your rua address will show every IP address that sent mail claiming to be from your domain, and whether each message passed SPF, DKIM, and DMARC alignment. This is where undiscovered third-party senders appear.
The reports are XML and not easy to read raw. Several free tools parse them: dmarcian, MXToolbox DMARC analyser, and similar. You are looking for rows with dkim=fail and spf=fail together on the same message, from IP addresses you do not recognise. Each one is a sender that will be affected when you enforce.
Once reports show only expected senders and all legitimate sources are passing, move to quarantine, then reject. Do not jump straight to reject.
Quarantine policy (intermediate step)
_dmarc.contoso.com TXT "v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc-reports@contoso.com; fo=1"
Starting with pct=25 means only a quarter of failing mail is quarantined; the rest passes through. Increase pct over successive weeks (25, 50, 100) while watching for reports of legitimate mail being caught. Once at 100 with no issues, move to reject:
Full enforcement
_dmarc.contoso.com TXT "v=DMARC1; p=reject; rua=mailto:dmarc-reports@contoso.com; fo=1"
Once DKIM is enabled and DMARC is published, send a test message from the domain and inspect the headers of the received message. Look for dkim=pass and dmarc=pass in the Authentication-Results header. Paste the full headers into the header analyser to read the results in one view, or use the email auth analyser to check the published DNS records directly.
Get-DkimSigningConfig | Select-Object Domain, Enabled, Status
This lists all domains with DKIM configured in the tenant. Any domain that sends mail as a From address should appear here with Enabled: True and Status: Valid.
| Error or symptom | Cause and fix |
|---|---|
| Status: CnameMissing after enabling | DNS has not propagated yet, or the CNAME values were entered incorrectly. Verify with Resolve-DnsName and compare to the values from Get-DkimSigningConfig. |
| Legitimate mail going to quarantine after DMARC enforcement | A third-party sender not covered by SPF or DKIM. Identify it from aggregate reports, then either add it to SPF or configure DKIM signing on that platform. |
| dkim=fail in headers despite Status: Valid | Message was modified in transit (some mailing lists rewrite headers or body). DKIM will fail on any alteration after signing. This is expected for list-forwarded mail; ARC sealing handles it for mail forwarded via Exchange. |
| SPF lookup limit exceeded (permerror) | More than 10 DNS lookups in the SPF record. Flatten includes using a service like dmarcian's SPF surveyor, or switch to DKIM-only alignment for affected senders. |
| DMARC reports not arriving | The rua address must be reachable. If using a different domain for reports (e.g. a reporting service), that domain must publish a _dmarc DNS record authorising reports for your domain. |