Assigning SCL to messages scanned by 3rd-party antispam filters

by Bharat Suneja

Here’s a problem I had a hard time resolving on Exchange Server 2003. Exchange Server 2007’s Transport Rules resolve this within minutes.

Pretend you’re taking a Microsoft exam:
Scenario: “You are the Exchange administrator for your organization… “.

Exchange has the Content Filter Agent (CFA), and the Edge Transport Server role designed to be a non-domain-joined mail gateway, located in perimeter networks. However, the Unix/Linux/Security folks in your organization don’t trust Exchange to do the filtering (or act as the mail gateway). They insist on using open source anti-spam software, such as SpamAssasin (yes Eric, I remember CRM114 – the Controllable Regex Mutilator… and all the cool stuff you can command it to do. Just to set the record straight, I was suitably impressed back then, and continue to be so till this day.. :) on the non-Exchange SMTP gateways. After tweaking it for a number of weeks, they are able to make it work the way they want it to, or are close to it. It’s implementation time!

Their solution is to insert an X-header in messages that looks like this:


That’s it. Their job ends there.

As the Exchange team/administrator, your job is to ensure messages with that header end up in users’ Junk Mail folder.

Exchange Server 2003 did not provide any way, out-of-the-box, to be able to inspect message headers and stamp SCL. To add some contextual fun – back then, our “solution” was a doc that showed the end-users how to create an Outlook rule to move such messages to Junk Mail. Only if Exchange/Windows admins could code… I can hear you think.

1 Creating a Transport Rule: Exchange Server 2007’s Transport Rules functionality allows you to accomplish this easily. Here’s how:
1. Fire up EMC | Organization Config | Hub Transport | Transport Rules tab
2. Click on New Transport Rule in the Action pane
3. Give the new rule a name, add a comment if you wish
4. In the Conditions page, select the condition when a message header contains specific words
5. In the Step 2 edit box, click on the message header link
Using a Transport Rule to inspect message headers
6. Type X-Spam-Status | click OK
7. In the edit box, click on the specific words link
8. Type yes | click OK | click Next
9. In the Actions page, select the action set the spam confidence level to value
10. In the rule description, click on the 0 link and add a value that’s above your SCLJunkThreshold | click Next
11. On the Exceptions page, click Next if you do not want any exceptions to this rule
12. Click New | click Finish to close the wizard

Or you can use the following commands:

$condition = Get-TransportRulePredicate HeaderContains
$condition.MessageHeader = “X-Spam-Status”
$condition.words = @(“yes”)
$action = Get-TransportRuleAction SetSCL
$action.SCLValue = 5
new-TransportRule “Stamp SCL” -condition @($condition) -action @($action)

2 Disabling the Content Filter agent: Since you have a 3rd-party filtering solution running on your non-Exchange SMTP host(s), you can disable the Content Filter Agent. Messages exceeding SCLJunkThreshold will still be moved to Junk Mail folder.

Disable-TransportAgent “Content Filter Agent”

Alternatively, you can leave the CFA enabled, but disable the Delete, Reject and Quarantine actions.

Set-ContentFilterConfig -SCLDeleteEnabled $false -SCLRejectEnabled $false -SCLQuarantineEnabled $false

Send a test message with the X-header X-Spam-Status:yes. The message has the SCL value set by the Transport Rule. If it is above the SCLJunkThreshold, it should be delivered to the Junk Mail folder.

Why this doesn’t work with Delete, Reject or Quarantine thresholds enabled?

The Content Filter Agent fires on the EndOfData SMTP event. On Hub Transport servers, the Transport Rules Agent fires on the OnRoutedMessage event. If the CFA is left enabled, along with any of the above actions, messages may get deleted, rejected or quarantined. The Transport Rules Agent never gets to see deleted and rejected messages at all.

What about quarantined messages?

It’s understandable if the message is already deleted or rejected – there’s nothing left for the Transport Rule agent to act on! However, why doesn’t the agent act on quarantined messages? With Pipeline Tracing turned on, I spent some precious cycles trying to unravel the mystery, with little success. As it turned out (thanks to helpful Exchange team folks for pointing out… ), I was overlooking the obvious – Quarantined messages are wrapped in an NDR “envelope”. If you’ve ever logged into the quarantine mailbox and looked at quarantined messages, you know what these look like. (Related post: “HOW TO: Expose original senders and recipients of quarantined messages“) The Transport Rules Agent does not see the original message headers, including our X-Spam-Status header. As a result, the rule never fires.

Life on the Edge

If using an Edge Transport server in the mix, things change. The Edge Rule agent fires on the EndOfData event, unlike Hub Transport servers. When two transport agents fire on the same SMTP event, you can set the priorities of each accordingly so one fires before the other. This is described in “How to Make the SCL Value Available to Edge Transport Rules” in Exchange Server 2007 documentation.

However, it’s a lot simpler to just disable the Content Filter Agent. This meets the requirements of this scenario, where anti-spam filtering is done by 3rd-party anti-spam filters and Exchange isn’t required to do any filtering at all.

{ 6 comments… read them below or add one }

Anonymous January 23, 2008 at 1:21 pm

good job!


Eggy July 23, 2008 at 8:40 am

Thanks. Very helpful. But in my case it doesn’t work — messages are *not* getting moved to Junk E-Mail folder. My Hub Transport rule (to set SCL) is firing, but messages is not getting moved. Any idea of why? More details are below.


– Hub Transport rule implemented to set SPAM messages’ SCL to 9.
– SCLJunkThreshold set to 8.
– SCLJunkEnabled:$true.
– CFA Disabled and CFA Delete/Reject/Quarantine are disabled.
– Settings for ‘Junk E-Mail processing within OWA seem irrelevent (IE-Fails regardless of settings).


Anonymous March 26, 2009 at 2:45 pm

You mentioned that you had a hard time making this work on Exchange 2003…what steps did you have to go through to make it work there?


brew September 14, 2009 at 8:21 am

I have a similar senario, where I have an open source smtp gateway that is filtering spam for my single 2007 Exchange Server. However, more and more spam is getting through so I thought I would implement the content filter agents in exchange server and let it run as a second level of spam filtering. Indeed it is working and filtering spam, but I can't get it to quarantine the spam to my quarantine mailbox. The mailbox works (I can send email to it) and the logs show spam supposedly getting quarantined for an SCL of 7 or higher ( Quarantine is set to 7 and True, Reject is set to 8 and false, and Delete is set to 9 and false). Looking though the Event Log I find lots of Event 9667 "Failed to create a new named property…" The named property is "OriginalScl". Researching the event 9667 tells me I need to reconfigure my Named Properties Quota in the registry. This event started at the same time that I enabled anti-spam filtering, so this is very suspicious. The quota is set to 8192 which I think is default (I have never changed it anyway).

Have you seen this before? I am reluctant to change the quota because I will have to dismount/remount my database to the quota size change to take effect.


Bharat Suneja September 14, 2009 at 9:19 am

@Alan: Doubt this is due to the content filter itself. It's possible that you were nearing the named properties quota limit at around the same time you enabled the content filter. You may want to look at Exchange 2007 SP2 and the change it implements for named properties. More in Exchange 2007 SP2 Released, Adds PowerShell v2 and Exchange 2010 Support,


brew September 14, 2009 at 11:50 am

@Bharat: Thanks for the SP2 tip. Any idea why my quarantined email is not landing in my quarantine mailbox? I'm stumped.


Leave a Comment

Previous post:

Next post: