Mail flow rules are the Swiss army knife of Exchange Online, and like any sharp tool they cause the most damage when used casually: a disclaimer that stacks up on every reply, a "[EXTERNAL]" prefix that breaks conversation threading, or an allow rule that quietly turns off spam filtering for a whole domain. In this post I'll explain how rules are evaluated, when to use the built-in External tag instead of a rule, which rules are genuinely useful, and how to test and audit them so they don't backfire.
How it works#
Every message that passes through your organization, except system-generated messages such as NDRs and journal reports, is evaluated against the enabled mail flow rules. The parts that decide the outcome:
- Priority is the processing order. In the Exchange admin center it's the position in the list; in PowerShell it's a number where 0 is the highest priority. New rules go to the bottom by default.
- Conditions are combined with AND. Multiple values inside one condition are OR. If you need "condition A or condition B", create two rules.
- Exceptions are combined with OR and always win: one matching exception stops the actions, even if every condition matched.
- Actions all apply. Some actions, such as deleting or forwarding the message, prevent later rules from running. Stop processing more rules does that deliberately.
- Mode is Enforce, Test with Policy Tips or Test without Policy Tips. Only Enforce changes delivery.
Three practical facts from Microsoft's documentation shape everything below: a new or changed rule can take up to 30 minutes to take effect, Exchange Online keeps no history of rule changes so you can't roll back, and the limits are 300 rules per organization, 8 KB per rule and 20 KB for all regular expressions and simple expressions combined.
Step 1: Tag external mail natively, not with a subject prefix#
Exchange Online has a built-in External sender identification feature that adds an "External" indicator in Outlook, Outlook for Mac, Outlook on the web and Outlook for iOS and Android. It doesn't rewrite the subject, so threading, search and reply chains stay intact. Microsoft's own guidance is to disable any subject-prefix rules before enabling it to avoid duplication.
Get-ExternalInOutlook
Set-ExternalInOutlook -Enabled $true
# Trusted partner senders or domains that shouldn't get the tag (uses the From address)
Set-ExternalInOutlook -AllowList @{Add="newsletter@fabrikam.com","*.contoso-partners.com"}Expect 24 to 48 hours before users see the tag. The allow list takes up to 200 entries and 8 KB in total, which is a hint that it's for a handful of trusted sources, not a bulk exclusion list.
Step 2: If you still need a banner, build the rule defensively#
Some organizations want an explicit warning text for external mail in addition to the tag. Prepend a disclaimer rather than touching the subject, and add an exception for the banner's own text so replies don't accumulate copies (Microsoft's test-rules article recommends exactly this trick for disclaimers). Use Ignore as the fallback action: Microsoft advises against Wrap for rules that act on inbound external mail because it interferes with Safe Attachments scanning.
New-TransportRule -Name "External sender banner" `
-FromScope NotInOrganization -SentToScope InOrganization `
-ApplyHtmlDisclaimerLocation Prepend `
-ApplyHtmlDisclaimerText '<p style="background:#fff4ce;padding:6px">CAUTION: This message came from outside Contoso. Verify the sender before clicking links or opening attachments.</p>' `
-ApplyHtmlDisclaimerFallbackAction Ignore `
-ExceptIfSubjectOrBodyContainsWords "This message came from outside Contoso" `
-Mode Audit -SetAuditSeverity Low -Comments "Owner: Messaging team"Step 3: Other rules that earn their place#
Warn on display-name impersonation#
The right tool for executives being impersonated is impersonation protection in Defender for Office 365 anti-phishing policies, which also covers look-alike domains and uses mailbox intelligence. If you only have Exchange Online Protection, a narrow rule can at least flag external mail whose From header carries a protected display name. Keep the list short and review it; every name is a regular expression evaluated on every inbound message.
New-TransportRule -Name "Warn: external mail using an executive display name" `
-FromScope NotInOrganization `
-HeaderMatchesMessageHeader From -HeaderMatchesPatterns "Adele Vance","Alex Wilber" `
-PrependSubject "[Possible impersonation] " -SetAuditSeverity High -Mode AuditBlock executable attachments#
The common attachments filter in the anti-malware policy is the first stop. A rule adds a custom rejection text and a clean audit trail, and AttachmentHasExecutableContent inspects the file's properties rather than trusting the extension.
New-TransportRule -Name "Block executable attachments from outside" `
-FromScope NotInOrganization -AttachmentHasExecutableContent $true `
-RejectMessageReasonText "Executable attachments are not accepted. Please share the file another way." `
-SetAuditSeverity Medium -StopRuleProcessing $trueBypass spam filtering, only when you must#
Setting the spam confidence level to -1 switches off most of EOP for matching messages. Microsoft's documentation allows it for trusted internal sources such as scanners, and explicitly warns against bypassing filtering for all internal mail because a compromised account would sail through. Scope it to the connecting IP of a device you control, never to a sender domain alone, and prefer a certificate- or IP-based connector plus the Tenant Allow/Block List for everything else.
New-TransportRule -Name "Bypass spam filtering: on-prem scanner relay" `
-SenderIpRanges 203.0.113.10 -SenderDomainIs contoso.com `
-SetSCL -1 -SetAuditSeverity Low -Comments "Scanner relay only. Review quarterly."Step 4: Order the rules on purpose#
Get-TransportRule | Sort-Object Priority | Format-Table Priority, Name, State, Mode, StopRuleProcessing
Set-TransportRule -Identity "Block executable attachments from outside" -Priority 0Put rejecting and blocking rules at the top with Stop processing more rules, then modifying rules such as banners and disclaimers, then anything that only audits. A rule with no conditions and no exceptions applies to every message in the organization; the EAC warns you, PowerShell doesn't.
Step 5: Test before you enforce#
Create the rule in Test without Policy Tips (-Mode Audit) and add the Generate incident report action so you receive a message each time it matches. Wait at least 30 minutes, then send the test matrix Microsoft suggests: messages you expect to match, messages you don't, internal and external senders, replies, and combinations that touch more than one rule.
Set-TransportRule -Identity "External sender banner" -Mode Audit `
-GenerateIncidentReport mailflow-tests@contoso.com `
-IncidentReportContent Sender,Recipients,Subject,RuleDetections
# When the results look right: switch to Enforce and remove the incident report action in the EAC
Set-TransportRule -Identity "External sender banner" -Mode EnforceTest with Policy Tips (AuditAndNotify) pairs with the Notify the sender with a Policy Tip action for rules that should coach users in Outlook before mail leaves.
Step 6: Audit changes and keep a backup#
Rule changes are recorded in the unified audit log, and because there's no version history, export the collection whenever you change something significant.
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) -RecordType ExchangeAdmin `
-Operations New-TransportRule,Set-TransportRule,Remove-TransportRule,Enable-TransportRule,Disable-TransportRule |
Select-Object CreationDate, UserIds, Operations
$export = Export-TransportRuleCollection
[System.IO.File]::WriteAllBytes("$PWD\TransportRules-backup.xml", $export.FileData)Verify#
- Message trace: open the test message's details and look for the Transport rule event; the Action column shows what the rule did.
- Exchange transport rule report: shows how often each rule matches, but only for rules whose severity isn't Not specified, which is why every example above sets
SetAuditSeverity. Most data appears within 24 hours; some can take up to five days. - Incident reports arriving in your test mailbox confirm the conditions match what you intended before anything is enforced.
Tips & gotchas#
- Allow-listing by sender domain is the classic mistake: the From domain is trivially spoofed, so a rule that bypasses filtering for "partner.com" is an open door. Authenticate the source (connector, IP, or the Tenant Allow/Block List) instead.
- Rules that bypass EOP should be few, documented in
Commentswith an owner, and reviewed on a schedule. - Disclaimers on every reply: always add the unique-phrase exception.
- Distribution groups: the "sent to this person" condition doesn't match groups; use "sent to a member of this group" instead.
- Size limits: long regular expression lists exhaust the 20 KB budget quickly. Keep patterns short and prefer exact-word conditions.
- Nothing happening? Wait the 30 minutes, check that a higher-priority rule isn't stopping processing, and confirm the rule is enabled and in Enforce mode.