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:
- Variables select transaction data to inspect—in this example, request arguments.
- Operator evaluates the selected data—here, a regular expression.
- Actions define when the rule runs, its metadata, logging, and what happens on a match.
Processing phases
| Phase | Common use |
|---|---|
| 1 | Request headers and early connection/request decisions |
| 2 | Request body and application-input inspection |
| 3 | Response headers |
| 4 | Response body inspection |
| 5 | Logging 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
- Disruptive:
deny,block,drop,redirect,allow - Logging:
log,nolog,auditlog,noauditlog,logdata - Metadata:
id,msg,tag,severity,rev,ver - Flow:
chain,skip,skipAfter - State and control:
setvar,initcol,expirevar,ctl
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.