Migrating from v2 to v3
This page is for administrators moving a working ModSecurity v2 (Apache mod_security2 or IIS) deployment to libModSecurity v3, typically because the web server changes to NGINX. Both versions are maintained by OWASP and implement the same rule language, so rule sets such as the OWASP CRS run on both; the differences are in the architecture, in a few evaluation details and in the legacy features v3 does not implement. If you stay on Apache httpd or IIS there is no need to migrate: v2 remains the supported choice there (current release 2.9.15).
What changed architecturally
Library plus connectors. v2 is a web server module and contains the engine, the Apache glue and the IIS module in one tree. v3 (current release 3.0.17) is only the engine, libmodsecurity, with a C and a C++ API. The web server integration is a separate connector project with its own release cycle: ModSecurity-nginx for NGINX (production ready) and ModSecurity-apache (not production ready, so Apache users should stay on v2). All Apache dependencies (APR, apxs) are gone from the engine. See Building libModSecurity v3 from source and libModSecurity v3 with NGINX.
Configuration scopes. In v2 the rules live in Apache’s configuration and inherit through <VirtualHost>, <Location>, <Directory> and .htaccess contexts. In v3 the connector loads the rules: with ModSecurity-nginx you point modsecurity_rules_file (or modsecurity_rules / modsecurity_rules_remote) at a file in an http, server or location block, and you can refine the global set per location with SecRuleRemoveById and similar directives. SecRuleInheritance On is rejected by the v3 parser.
Persistent storage. v2 keeps the IP, GLOBAL, RESOURCE, SESSION and USER collections in SDBM files under SecDataDir, shared by all worker processes. v3 keeps them in memory per process by default; the data is not shared between NGINX workers and is lost on restart. Build with --with-lmdb for an LMDB-backed shared store. SecDataDir is accepted but ignored. See Persistent Storage.
Request body buffering. SecRequestBodyInMemoryLimit is rejected at load time by v3, which follows the web server’s own buffering configuration (client_body_buffer_size in NGINX). Remove the directive from a configuration copied from v2.
Logging. v3 writes the same audit log formats (native and JSON, serial and concurrent) and a debug log, but there is no Apache error log: alerts go through the connector to the web server’s error log. mlogc, the v2 log collector, is not part of v3; ship the JSON audit log with a generic log forwarder instead. See Logging and Data Formats.
Base configuration. Start from the v3 modsecurity.conf-recommended, not the v2 file; the differences are listed in Recommended Base Configuration.
Rule evaluation differences to check in custom rules
- Default phase. A rule without
phase:runs in phase 2 in v2 but in phase 1 in v3. Setphase:on every rule. SecDefaultAction. v3 allows oneSecDefaultActionper phase and rejects a second one, requires it to name a real disruptive action (blockis not accepted there), and applies its disruptive action to a rule only throughblock; a rule without any disruptive action does not inherit one. v2 lets a laterSecDefaultActionreplace an earlier one and applies its disruptive action to every rule that has none.- Phase 4 without body inspection. v2 runs phase 4 rules with an empty
RESPONSE_BODY; v3 skips phase 4 whenSecResponseBodyAccessis Off or the MIME type is not inspected. skipAfter. If the namedSecMarkerdoes not exist, v3 skips every remaining rule of every later phase; v2 ends the skip with the phase.ctl:ruleEngine=Off. v2 stops the current phase immediately; v3 only checks the engine state when a phase starts.SecRuleRemoveByTag/SecRuleRemoveByMsg. v2 matches the parameter as an unanchored regular expression; v3 compares the whole tag or message literally.initcolandsetvarin one rule. v3 runssetvarbeforeinitcolwithin the same rule; initialize the collection in a separate, earlier rule.SecUploadKeepFiles RelevantOnlyis rejected by v3; useOnorOff.SecCookieFormat 1is not supported by v3.SecCollectionTimeout,SecStatusEngineandSecPcreMatchLimitRecursionare accepted by v3 but ignored, so a configuration that relies on collection expiry needsexpirevarinstead.
See Processing Phases for details.
Not supported in v3
The lists below contain the v2 features that libModSecurity v3 does not implement, verified against the v3.0.17 parser and cross-checked with the compatibility matrix of the SecLang specification (see SecLang specification). Most of them are tied to Apache internals or to retired services and are marked deprecated in the specification; no current rule set uses them.
Directives
Rejected by the v3 parser at load time (remove them from the configuration):
- SecCacheTransformations - experimental in v2, dropped.
- SecChrootDir
- SecConnReadStateLimit and SecConnWriteStateLimit - Apache connection-state limits; use the web server’s own limits (for example
limit_connin NGINX). The older spellings SecReadStateLimit and SecWriteStateLimit are unknown to v3. - SecGsbLookupDb and the
@gsbLookupoperator - the Google Safe Browsing v1 API they used no longer exists. - SecGuardianLog
- SecHashEngine
On, SecHashKey, SecHashParam, SecHashMethodRx, SecHashMethodPm - SecRequestBodyInMemoryLimit
- SecServerSignature
- SecStreamInBodyInspection and SecStreamOutBodyInspection, with the
STREAM_INPUT_BODY/STREAM_OUTPUT_BODYvariables and the@rsuboperator that depends on them. - SecUnicodeCodePage - use the code page argument of
SecUnicodeMapFileinstead. - SecRequestEncoding - accepted by v2 but never implemented.
Onis rejected for SecConnEngine, SecContentInjection, SecDisableBackendCompression, SecInterceptOnError and SecRuleInheritance;Offis accepted. SecSensorId and SecRulePerfTime are rejected.
Variables
- MULTIPART_CRLF_LINE
PERF_*rule-timing variables: PERF_ALL, PERF_COMBINED, PERF_GC, PERF_LOGGING, PERF_PHASE1 throughPERF_PHASE5, PERF_RULES, PERF_SREAD, PERF_SWRITESCRIPT_*variables (Apache file metadata): SCRIPT_BASENAME, SCRIPT_FILENAME, SCRIPT_GID, SCRIPT_GROUPNAME, SCRIPT_MODE, SCRIPT_UID, SCRIPT_USERNAME- SDBM_DELETE_ERROR - no SDBM in v3.
- STATUS_LINE exists in v3 but holds only the status code (it is an alias of
STATUS); useRESPONSE_PROTOCOLfor the protocol. - STREAM_INPUT_BODY and STREAM_OUTPUT_BODY
- USERAGENT_IP
- WEBSERVER_ERROR_LOG - Apache error log messages are not visible to the library.
Actions
- append and prepend (content injection)
- deprecatevar
- marker
- pause
- proxy - relied on Apache mod_proxy.
- sanitiseArg, sanitiseMatched, sanitiseMatchedBytes, sanitiseRequestHeader, sanitiseResponseHeader - audit log masking.
- The following ctl options, which the v3 parser rejects:
debugLogLevel,hashEnforcement,hashEngine,requestBodyLimit,responseBodyAccess,responseBodyLimit,ruleRemoveByMsg,ruleRemoveTargetByMsg. v3 implementsauditEngine,auditLogParts,parseXmlIntoArgs,requestBodyAccess,requestBodyProcessor,ruleEngine,ruleRemoveById,ruleRemoveByTag,ruleRemoveTargetByIdandruleRemoveTargetByTag, and acceptsforceRequestBodyVariableas a no-op (REQUEST_BODYis always populated).
Operators
- gsbLookup, rsub and validateHash are rejected by the v3 parser at load time (
Operator: ... is not yet supported). No other v2 operator is missing.
Transformations
All v2 transformation functions are available in v3. (t:uppercase exists in v3 but not in v2.)
Checklist
- Install libmodsecurity and the connector for your web server.
- Start from the v3
modsecurity.conf-recommended; copy only your site-specific values (limits, audit log location, MIME types) from the old file. - Remove the directives listed above from your configuration; the v3 parser refuses to load a file that contains most of them.
- Grep your custom rules for the variables, actions and
ctloptions listed above and for rules withoutphase:. - If you depend on
IP,GLOBALor other persistent collections (for example the CRS DoS-protection plugin), build with--with-lmdbor accept per-worker counters. - Run in
SecRuleEngine DetectionOnly, compare the audit log with the v2 deployment, then switch toOn. - Report anything that behaves differently and is not listed here at https://github.com/owasp-modsecurity/ModSecurity/issues.