Migrating from CRS 3.3 to CRS 4.25 LTS — Part 3: The Plugin Architecture

This is Part 3 of the CRS 3.3 → 4.25 LTS migration series. Part 2 covered crs-setup.conf changes. This post covers the plugin architecture — the most structurally significant change in CRS 4, and the one that requires the most hands-on action from operators who used application exclusion packages in CRS 3.

The Key Change: Application Exclusions Are No Longer in Core

In CRS 3.3, the release tarball included a set of optional rule exclusion packages. If you ran WordPress, Nextcloud, phpBB, phpMyAdmin, Drupal, or a handful of other applications, you could include these files to suppress false positives specific to those applications:

rules/REQUEST-903.9001-WORDPRESS-EXCLUSIONS.conf
rules/REQUEST-903.9002-CPANEL-EXCLUSIONS.conf
rules/REQUEST-903.9003-NEXTCLOUD-EXCLUSIONS.conf
rules/REQUEST-903.9004-DOKUWIKI-EXCLUSIONS.conf
rules/REQUEST-903.9005-CPANEL-EXCLUSIONS.conf
rules/REQUEST-903.9006-XENFORO-EXCLUSIONS.conf
rules/REQUEST-903.9007-PHPBB-EXCLUSIONS.conf
rules/REQUEST-903.9008-PHPMYADMIN-EXCLUSIONS.conf
...

In CRS 4, these files are gone from the release tarball. They have been moved to independent plugin repositories. If you relied on any of these files in CRS 3, they will simply be absent after upgrading to CRS 4 unless you explicitly install the corresponding plugins.

This change was intentional and security-motivated. Having application exclusion rules bundled in core meant that a bug in, say, the WordPress exclusion package affected every CRS installation — including those that do not run WordPress. By moving exclusions to opt-in plugins, the default attack surface is smaller. (A previous incident where a bundled exclusion package had a critical vulnerability validated this reasoning.)

Modular architecture: from monolith to plugins

Modular architecture: from monolith to plugins Markus Spiske on Pexels

What Is the Plugin Architecture?

A plugin is a set of rules packaged as three optional files:

plugins/<plugin-name>-config.conf   # plugin configuration
plugins/<plugin-name>-before.conf   # rules to run before CRS rules
plugins/<plugin-name>-after.conf    # rules to run after CRS rules

These files are loaded via three Include statements that must be present in your WAF configuration, positioned around the main CRS rules Include:

Include crs/crs-setup.conf

Include crs/plugins/*-config.conf
Include crs/plugins/*-before.conf

Include crs/rules/*.conf

Include crs/plugins/*-after.conf

This load order matters. Rules in *-before.conf run before CRS has processed the request — this is where exclusion rules go, so they can suppress matching before CRS rules fire. Rules in *-after.conf run after CRS — this is where scoring rules that need to know the CRS result go.

The three Include directives for plugins are new in CRS 4. If you are upgrading an existing installation, you must add them to your WAF configuration. The CRS release includes three empty placeholder files in the plugins/ directory so the Include statements do not produce errors when no plugins are installed.

The Plugin Registry

The CRS Plugin Registry is the central catalogue of all known CRS plugins — official, community-contributed, and third-party. It serves two purposes:

  1. Discovery: it lists every available plugin with a description, repository link, and quality tier.
  2. Namespace management: it tracks which rule ID sub-range (within 9,500,000–9,999,999) has been assigned to each plugin, preventing conflicts between plugins from different authors.

Before installing any plugin, check the registry to confirm you are using the current repository URL and to see whether the plugin is listed as tested, in-testing, or draft. Quality tiers matter: tested plugins have gone through the same review process as CRS core rules; draft plugins may have breaking changes or incomplete coverage.

When you write your own rules or custom plugins, reserve your rule ID range through the registry to avoid conflicts with plugins you may install later.

Mapping: CRS 3 Exclusion Packages to CRS 4 Plugins

Here is the complete mapping between CRS 3 application exclusion rule files and their CRS 4 plugin equivalents, sourced directly from the registry:

CRS 3 fileCRS 4 pluginRule ID rangeRepository
REQUEST-903.9001-WORDPRESS-EXCLUSIONS.confwordpress-rule-exclusions-plugin9,507,000–9,507,999coreruleset/wordpress-rule-exclusions-plugin
REQUEST-903.9002-CPANEL-EXCLUSIONS.confcpanel-rule-exclusions-plugin9,510,000–9,510,999coreruleset/cpanel-rule-exclusions-plugin
REQUEST-903.9003-NEXTCLOUD-EXCLUSIONS.confnextcloud-rule-exclusions-plugin9,508,000–9,508,999coreruleset/nextcloud-rule-exclusions-plugin
REQUEST-903.9004-DOKUWIKI-EXCLUSIONS.confdokuwiki-rule-exclusions-plugin9,509,000–9,509,999coreruleset/dokuwiki-rule-exclusions-plugin
REQUEST-903.9005-DRUPAL-EXCLUSIONS.confdrupal-rule-exclusions-plugin9,506,000–9,506,999coreruleset/drupal-rule-exclusions-plugin
REQUEST-903.9006-XENFORO-EXCLUSIONS.confxenforo-rule-exclusions-plugin9,511,000–9,511,999coreruleset/xenforo-rule-exclusions-plugin
REQUEST-903.9007-PHPBB-EXCLUSIONS.confphpbb-rule-exclusions-plugin9,512,000–9,512,999coreruleset/phpbb-rule-exclusions-plugin
REQUEST-903.9008-PHPMYADMIN-EXCLUSIONS.confphpmyadmin-rule-exclusions-plugin9,513,000–9,513,999coreruleset/phpmyadmin-rule-exclusions-plugin

Note that CRS 3 did not ship Drupal exclusions as a standalone numbered file in all releases — if you had a Drupal exclusion file, it maps to drupal-rule-exclusions-plugin. Verify your specific CRS 3.3.x tarball contents if you are unsure which files you had.

Community-Contributed Rule Exclusion Plugins

The registry also lists third-party plugins for applications that never had CRS 3 bundled exclusions:

ApplicationRule ID rangeRepositoryMaintainer
Roundcube9,519,000–9,519,999EsadCetiner/roundcube-rule-exclusions-pluginEsadCetiner
SOGo9,520,000–9,520,999EsadCetiner/sogo-rule-exclusions-pluginEsadCetiner
iRedAdmin9,521,000–9,521,999EsadCetiner/iredadmin-rule-exclusions-pluginEsadCetiner
Plausible Analytics9,528,000–9,528,999EsadCetiner/plausible-rule-exclusions-pluginEsadCetiner

For any application not listed here, check the registry directly — it is updated as new plugins are submitted. If no plugin exists for your application, you will need to write your own or re-create the relevant exclusions manually in REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.

How to Install a Plugin

Method 1: Copy the files (simple)

Download the plugin repository and copy the .conf and data files into your plugins/ directory:

# Example: installing the WordPress exclusions plugin
git clone https://github.com/coreruleset/wordpress-rule-exclusions-plugin.git /tmp/wp-plugin
cp /tmp/wp-plugin/plugins/* /path/to/crs/plugins/

# If there is a .example config file, rename it
mv /path/to/crs/plugins/wordpress-rule-exclusions-plugin-config.conf.example \
   /path/to/crs/plugins/wordpress-rule-exclusions-plugin-config.conf

Review the config file for any options to set, then reload your WAF.

Clone the plugin to a permanent location and create symlinks in plugins/. This makes updates as simple as git pull:

git clone https://github.com/coreruleset/wordpress-rule-exclusions-plugin.git \
    /opt/crs-plugins/wordpress-rule-exclusions-plugin

# Rename the example config
cp /opt/crs-plugins/wordpress-rule-exclusions-plugin/plugins/wordpress-rule-exclusions-plugin-config.conf.example \
   /opt/crs-plugins/wordpress-rule-exclusions-plugin/plugins/wordpress-rule-exclusions-plugin-config.conf

# Symlink each file into the CRS plugins directory
for f in /opt/crs-plugins/wordpress-rule-exclusions-plugin/plugins/*; do
    ln -s "$f" /path/to/crs/plugins/
done

To update: git -C /opt/crs-plugins/wordpress-rule-exclusions-plugin pull and reload.

Enabling Collections for Plugins

Some plugins (the fake-bot plugin, the auto-decoding plugin) need the GLOBAL and IP collections initialized before they can run. In CRS 4 these collections are not initialized by default — you must set tx.enable_default_collections=1 in your crs-setup.conf to enable them.

Check each plugin’s documentation for whether it needs collections. If any plugin you install requires them, enable the flag. It is safe to enable it even if only some of your plugins need it.

Using Plugins on Multi-Application Reverse Proxies

If your WAF fronts multiple applications, you likely do not want WordPress exclusion rules to apply to requests going to your API or your phpBB forum. CRS 4 plugins support per-virtual-host scoping via SecWebAppID (ModSecurity) or the Host header (Coraza):

# ModSecurity: disable the WordPress plugin for non-WordPress virtual hosts
SecRule &TX:wordpress-rule-exclusions-plugin_enabled "@eq 0" \
    "id:9507010,phase:1,pass,nolog,chain"
    SecRule WebAppID "!@streq wordpress" \
        "t:none,setvar:'tx.wordpress-rule-exclusions-plugin_enabled=0'"

Most official plugins include this pattern commented out in their config file. Review each plugin’s *-config.conf for the scoping example.

Capability Plugins (Not in CRS 3)

Beyond rule exclusions, the registry includes capability plugins that add functionality with no CRS 3 equivalent. These are worth knowing about even if you are not migrating from an exclusion package:

PluginRule ID rangeDescription
antivirus-plugin9,502,000–9,502,999Integrates ClamAV to scan uploaded files and optionally request bodies. Blocks on detection without raising the anomaly score.
fake-bot-plugin9,504,000–9,504,999DNS PTR verification for requests claiming to be Google, Bing, Amazon, Facebook, Apple, LinkedIn, and Twitter. Requires Lua.
body-decompress-plugin9,503,000–9,503,999Decompresses gzip/deflate response bodies for inspection by CRS rules.
auto-decoding-plugin9,501,000–9,501,999Applies additional decode transformations at PL3/PL4 to catch evasions through double-encoding.
dos-protection-modsecurity-plugin9,514,000–9,514,999Rate-based DoS protection using ModSecurity collections. Requires tx.enable_default_collections=1.
google-oauth2-plugin9,505,000–9,505,999Exclusions for Google OAuth2 flows.
incubator-plugin9,900,000–9,999,999Experimental rules being tested before promotion to core. Useful for running cutting-edge detections in detection-only mode.

Rule ID Namespace

The rule ID range 9,500,000–9,999,999 is reserved for CRS plugins. This is important if you have written any custom rules in that range — they may conflict with plugin rules. Check the plugin registry for the assigned sub-ranges, and renumber any conflicting custom rules before installing plugins.

What’s Next

Part 4 covers the anomaly scoring and reporting changes — including the variable renames, the new granular reporting model, early blocking, and how the redistribution of rules across paranoia levels affects your anomaly score baseline.

Related pages:

Felipe Zipitria