Rule language

ModSecurity rule language

Learn SecRule anatomy, variables, operators, transformations, actions, chains, phases, and macro expansion.

Adapted ModSecurity documentationThis page has been reorganized, rewritten, and supplemented by Atomicorp from upstream ModSecurity reference material. Source, license, and attribution details.

Anatomy of a rule

SecRule ARGS "@rx (?i:example)" \
  "id:100001,phase:2,deny,status:403,log,msg:'Example match'"

Every SecRule has three logical parts:

  1. Variables select transaction data to inspect—in this example, request arguments.
  2. Operator evaluates the selected data—here, a regular expression.
  3. Actions define when the rule runs, its metadata, logging, and what happens on a match.

Processing phases

PhaseCommon use
1Request headers and early connection/request decisions
2Request body and application-input inspection
3Response headers
4Response body inspection
5Logging after transaction processing

Choose the earliest phase where all required data is available. Body variables are not ready in phase 1.

Variables

Variables expose transaction data. Common examples include REQUEST_URI, REQUEST_HEADERS, ARGS, ARGS_GET, ARGS_POST, REQUEST_BODY, FILES, RESPONSE_HEADERS, RESPONSE_BODY, REMOTE_ADDR, TX, and MATCHED_VAR.

Collections such as ARGS expand to multiple values. Target a named member with ARGS:username or exclude a member from a larger target when appropriate.

Operators

Operators perform the comparison. @rx applies a regular expression, @streq performs a string comparison, @contains looks for a substring, @beginsWith and @endsWith test boundaries, @ipMatch handles addresses and networks, and @validateByteRange enforces an allowed byte set.

Some operators can be negated with !. Use negation carefully: it changes when the rule matches, not merely the message.

Transformations

Transformations normalize a copy of input before the operator evaluates it:

SecRule ARGS "@contains select" \
  "id:100002,phase:2,deny,t:none,t:urlDecodeUni,t:lowercase"

Order matters. Decode before case normalization when that is the intended interpretation. Begin with t:none when you need to clear inherited transformations.

Actions

Every locally written rule needs an ID that does not collide with vendor rules. Atomicorp’s assigned rule range is 300000–399999; do not use that range for local rules.

Chains

chain requires the next rule to match before the chain matches. Put disruptive actions and primary metadata on the chain starter. Chained rules are useful when multiple independent conditions must be true, but long chains can be difficult to debug.

Macro expansion

Actions can include transaction values using %{VARIABLE} or %{COLLECTION.MEMBER} syntax:

logdata:'Matched %{MATCHED_VAR_NAME}: %{MATCHED_VAR}'

Treat logged request data as potentially sensitive and untrusted.

For exhaustive keyword details, use the upstream references for variables, operators, transformations, and actions.