When Microsoft 365 email lands in the Junk Email folder, open the message headers and read three things: the SFV and CAT fields in the X-Forefront-Antispam-Report header, the BCL value in X-Microsoft-Antispam, and the compauth result in Authentication-Results. Together they tell you which component moved the message (content filtering, bulk filtering, spoof and DMARC enforcement, an Advanced Spam Filter setting, a mail flow rule, or the user's own lists), and each component has its own fix. Don't rely on the SCL value: in cloud organizations Microsoft states that it no longer determines the verdict or the action.
Who this is for and what you will have
This guide is for Exchange Online and Microsoft 365 administrators who get tickets like "a supplier's invoices go to Junk" or "the payroll provider's mail is never in the Inbox". It assumes mailboxes in Exchange Online, protected by the built-in anti-spam features (Exchange Online Protection) with or without Microsoft Defender for Office 365. At the end you will have:
- A repeatable way to read the anti-spam headers and identify which filter acted.
- The specific fix for each cause, from a false-positive submission to an anti-spam policy change.
- A way to verify the fix with a new message instead of guessing.
Prerequisites
- An example of an affected message, ideally still in the user's mailbox, or its network message ID.
- Exchange Online PowerShell (
Connect-ExchangeOnline) with an account that can read anti-spam policies and mailbox junk settings. Changing a user's junk email lists needs the Mail Recipients or User Options role. - Access to the Microsoft Defender portal at
https://security.microsoft.com. Creating Tenant Allow/Block List entries needs Organization Management or Security Administrator, or the equivalent Defender XDR unified RBAC permission. - The Message Header Analyzer at
https://mha.azurewebsites.netis optional but makes long headers readable.
Step 1: Get the full message headers
Get the internet headers from the copy that landed in Junk, not from a forwarded copy, because forwarding replaces the headers you need.
- Outlook for Windows: open the message, select File > Properties, and copy the Internet headers box.
- Outlook on the web: open the message, select ... (More actions) > View > View message details.
- Admin: find the message on the Message trace page in the Exchange admin center and view its details.
Paste the headers into the Message Header Analyzer if you want them split into fields. While you have them, note the value of X-MS-Exchange-Organization-Network-Message-Id. You need that GUID if you submit the message to Microsoft later.
Step 2: Read the headers that matter
X-Forefront-Antispam-Report
This header holds field and value pairs separated by semicolons, for example:
X-Forefront-Antispam-Report: CIP:198.51.100.50;CTRY:US;LANG:en;
SFV:SPM;IPV:NLI;SRV:;H:mail.fabrikam.com;PTR:mail.fabrikam.com;
CAT:SPM;SFTY:;DIR:INB;The SFV (spam filtering verdict) field is the primary indicator of what decided the message's fate:
| SFV value | What it means |
|---|---|
SFV:SPM | Spam filtering marked the message as spam |
SFV:NSPM | Spam filtering marked the message as not spam |
SFV:BLK | Sender is in the recipient's Blocked Senders list |
SFV:SFE | Sender is in the recipient's Safe Senders list; filtering skipped |
SFV:SKA | Sender or domain is on an anti-spam policy's allowed list |
SFV:SKB | Sender or domain is on an anti-spam policy's blocked list |
SFV:SKI | Source IP is on the connection filter IP Allow List (with IPV:CAL) |
SFV:SKN | A mail flow rule's request to bypass spam filtering was honored |
SFV:SKS | A mail flow rule (or on-premises Exchange in hybrid) marked it as spam before filtering |
SFV:SKQ | Released from quarantine |
The CAT field names the category of protection that applied, for example SPM (spam), HSPM (high confidence spam), BULK, SPOOF, PHSH (phishing) or HPHSH (high confidence phishing). DIR tells you whether the message was inbound (INB), outbound (OUT) or internal (INT). CIP is the connecting IP address, and SRV:BULK means the message was identified as bulk email.
The SCL field is still stamped, but Microsoft's documentation is explicit that in cloud organizations it doesn't determine whether a message is identified as spam or what action is taken; it exists mainly for on-premises Exchange, including hybrid delivery to the Junk Email folder. Two messages with the same SCL can have different verdicts, so use SFV and CAT for diagnosis.
X-Microsoft-Antispam and X-CustomSpam
X-Microsoft-Antispam contains BCL, the bulk complaint level. A higher BCL means a bulk message is more likely to generate complaints. If BCL meets or exceeds the threshold in the anti-spam policy, the message is treated as bulk.
An X-CustomSpam header appears only when an Advanced Spam Filter (ASF) setting flagged the message, and its value names the setting, for example X-CustomSpam: Web bug.
Authentication-Results
This header records SPF, DKIM and DMARC results and the composite authentication verdict:
Authentication-Results: spf=fail (sender IP is 198.51.100.77)
smtp.mailfrom=fabrikam.com; dkim=none;
dmarc=fail action=quarantine header.from=fabrikam.com;
compauth=fail reason=000compauth=fail reason=000 means the message failed DMARC and the sender's policy is p=quarantine or p=reject. reason=001 is an implicit failure: the sender's domain has no authentication records or only weak ones (SPF ~all or ?all, or DMARC p=none). reason=130 means a trusted ARC sealer's result overrode a DMARC failure.
Step 3: Match the evidence to the cause
| Header evidence | Component that acted | Where the fix is |
|---|---|---|
SFV:SPM, CAT:SPM or CAT:HSPM, no X-CustomSpam | Content filtering | False-positive submission and allow entry |
SFV:SPM with X-CustomSpam: present | An ASF setting | Turn the ASF setting off |
SRV:BULK or CAT:BULK, with a BCL value | Bulk filtering | Review the bulk threshold for the affected users |
CAT:SPOOF, compauth=fail | Spoof intelligence or DMARC enforcement | Sender authentication, or a spoofed sender allow entry |
SFV:BLK | User's Blocked Senders list | Remove the entry from the user's list |
SFV:SKS | Mail flow rule that set SCL 5 to 9 | Find and fix the rule |
SFV:NSPM, but the message is in Junk | Not the service verdict | Outlook client filter, user lists or zero-hour auto purge |
Two policy facts explain many surprises. With the default anti-spam policy, messages with the Spam, High confidence spam, Phishing and Bulk verdicts are all moved to the Junk Email folder (MoveToJmf); the Standard and Strict preset policies quarantine some of those instead. And messages that spoof intelligence detects as spoofed are moved to Junk by default in anti-phishing policies, unless the Strict preset applies.
Also check which policy applies to the recipient. Anti-spam policies are evaluated in a fixed order: Strict preset, then Standard preset, then custom policies by priority, then the default policy, and only the first match applies. A setting you changed in a custom policy has no effect on a user who is in a preset policy.
Connect-ExchangeOnline -UserPrincipalName admin@contoso.com
Get-HostedContentFilterRule | Sort-Object Priority |
Format-Table Name, Priority, State, SentTo, SentToMemberOf, RecipientDomainIsStep 4: Apply the fix for that cause
Content filtering false positive
Report the message to Microsoft and let the submission create the allow entry:
- In the Defender portal, go to Actions & submissions > Submissions (or
https://security.microsoft.com/reportsubmission), and on the Emails tab select Submit to Microsoft for analysis. - Enter the network message ID (or upload the .msg or .eml file) and choose at least one affected recipient, so the policy check runs against that recipient.
- Under Why are you submitting this message to Microsoft? select I've confirmed it's clean, then Next.
- Select Allow this message. Remove allow entry after defaults to 45 days after last used date; the other choices are 1, 7 or 30 days or a specific date up to 30 days ahead.
Allow entries appear on the Tenant Allow/Block Lists page a few moments later. They are created only for the parts of the message the filters found bad. If the sender address wasn't considered malicious, no sender allow entry is created, and the submission result is still useful as feedback.
You can also create sender allow entries directly. These override the bulk, spam, high confidence spam and phishing verdicts, but not high confidence phishing or malware:
New-TenantAllowBlockListItems -ListType Sender -Allow -Entries "billing@fabrikam.com"
Get-TenantAllowBlockListItems -ListType Sender -AllowWithout -ExpirationDate or -RemoveAfter, the entry expires 45 days after it was last used. The number of entries depends on licensing: 500 allow entries without Defender for Office 365, 1,000 with Plan 1 and 5,000 with Plan 2.
Bulk mail
The anti-spam policy's bulk email threshold (BulkThreshold) is 7 in the default policy, 6 in Standard and 5 in Strict; a message at or above it gets the bulk verdict. If a newsletter the business actually wants is caught because its BCL equals the threshold (for example BCL 7 against the default threshold of 7), create a custom anti-spam policy scoped to the users who want it rather than loosening the default policy for everyone:
Get-HostedContentFilterPolicy | Format-Table Name, BulkThreshold, BulkSpamAction, SpamAction
Set-HostedContentFilterPolicy -Identity "Marketing team" -BulkThreshold 8Remember that a preset policy covering those users wins over any custom policy.
Spoofing and DMARC failures
If CAT:SPOOF and compauth=fail appear, the message failed email authentication. The durable fix is at the sender: SPF that includes the sending service, DKIM signing with the From domain, and DMARC that aligns. When you are the sender (mail from your own domain arriving in another tenant's Junk), set up DKIM signing as described in DKIM CNAME records not found.
If the message passes through a mail gateway or service that modifies it before Microsoft 365, authentication often breaks in transit; ARC trusted sealers and Enhanced Filtering covers that case. For a legitimate third party you can't get fixed quickly, create an allow entry for the spoofed sender and its sending infrastructure. Spoofed sender entries never expire, so review them:
New-TenantAllowBlockListSpoofItems -Identity Default -Action Allow `
-SpoofedUser billing@fabrikam.com -SendingInfrastructure fabrikam.net -SpoofType ExternalThe sending infrastructure is the domain from the PTR record of the source IP, a verified DKIM domain, or the source IP as a /24 if there's no PTR record.
Advanced Spam Filter settings
Every ASF setting is off by default, and Microsoft no longer recommends enabling them. You also can't report messages that ASF flags as false positives. If X-CustomSpam names a setting, turn it off in the policy that applied:
Get-HostedContentFilterPolicy | Format-Table Identity, MarkAsSpam*, IncreaseScoreWith* -AutoSize
Set-HostedContentFilterPolicy -Identity "Default" -MarkAsSpamWebBugsInHtml OffThe user's own Blocked Senders list
SFV:BLK means the user (or an admin) blocked the sender. Check and remove the entry:
Get-MailboxJunkEmailConfiguration -Identity "user@contoso.com" | Format-List trusted*,contacts*,blocked*
Set-MailboxJunkEmailConfiguration -Identity "user@contoso.com" -BlockedSendersAndDomains @{Remove="fabrikam.com"}A mail flow rule
SFV:SKS points to a rule that set the SCL to mark messages as spam. List every rule that sets an SCL, and read its conditions against the message:
Get-TransportRule | Where-Object {$_.SetSCL -ne $null} | Format-Table Name, State, SetSCL, PriorityThe detailed message trace also shows an AGENTINFO event naming the rule that fired.
The verdict was "not spam" but the message is in Junk
If the header shows SFV:NSPM, the service delivered the message as clean. Check these instead:
- Outlook Junk Email Filter: when set to Low or High, Outlook applies its own classification after delivery, separately from the Microsoft 365 verdict. Microsoft recommends leaving it at No automatic filtering, which you can enforce with Group Policy.
- User lists processed by Outlook: Outlook still uses the Safe Senders and Blocked Senders lists to move messages after delivery.
- Zero-hour auto purge: if ZAP later decides a delivered message is spam or phishing, it moves it to Junk Email for policies whose action is Move message to Junk Email folder.
What not to do
It's tempting to create a mail flow rule that sets SCL to -1 for the sender's domain. Microsoft warns that setting SCL -1 creates significant risk if the conditions aren't carefully scoped, and a rule keyed only on a sender domain also applies to messages that spoof that domain. Such a rule also doesn't bypass malware scanning, and when your MX record points to Microsoft 365 it doesn't deliver high confidence phishing either. If you must use one, combine several conditions, for example sender address plus a specific header value. Use the advanced delivery policy, not a rule, for SecOps mailboxes and phishing simulations.
Similarly, avoid anti-spam policy allowed domains for common providers; attackers can spoof allowed domains. Prefer the Tenant Allow/Block List, whose entries expire and show a last-used date.
Verify the fix
- Ask the sender to send a new message; old messages keep their old headers.
- Read the new headers. The spam, bulk or spoof verdict you found in step 2 should be gone; a fixed DMARC problem shows
compauth=pass. - Confirm the message is in the Inbox for every affected recipient. Tenant Allow/Block List entries should be active within 5 minutes.
- On the Tenant Allow/Block Lists page, check the Last used date of the entry you created, so you know it is the thing that made the difference.
Troubleshooting
The allow entry exists but mail still goes to Junk. Check whether the user is in the Standard or Strict preset security policy; allowed sender lists in custom anti-spam policies are ignored for those users, so use the Tenant Allow/Block List instead. Also confirm the verdict isn't malware or high confidence phishing, which allow lists can't override.
Safe Senders entries aren't honored. If the applied policy quarantines the verdict instead of moving it to Junk, domain entries in a user's Safe Senders list aren't honored; individual email address entries are, unless the message is malware or high confidence phishing.
Outlook reports: "Cannot/Unable add to the server Junk E-mail lists. You are over the size allowed on the server." The user's safelist collection exceeded the 510 KB limit. Ask the user to remove old entries; the server-side Junk filter is disabled until the lists are reduced.
Only some recipients see the message in Junk. Different recipients can match different anti-spam policies, and each user has their own lists and Outlook settings. Compare the headers from two recipients.
The sender's own mail from your tenant lands in Junk elsewhere. That's the receiving organization's filter. Check your DKIM, SPF and DMARC first, then ask the recipient's admin for their headers.
Checklist
- Headers taken from the copy in Junk, not a forward.
SFV,CAT,BCL,X-CustomSpamandcompauthread;SCLignored for the diagnosis.- Applicable anti-spam policy confirmed, including preset policies.
- Fix applied at the component that acted: submission and allow entry, bulk threshold, sender authentication or spoof entry, ASF setting, user list, mail flow rule or Outlook client setting.
- No broad SCL -1 rule added.
- New test message delivered to the Inbox and its headers checked.
References
- Anti-spam message headers
- Spam confidence level (SCL)
- Troubleshoot anti-spam policies
- Configure junk email settings on Exchange Online mailboxes
- Recommendations for Microsoft 365 security settings
- Submit messages, URLs and attachments for analysis
- Allow or block email using the Tenant Allow/Block List
- Use mail flow rules to set the SCL in messages