Skip to main content

Rulesets

Rules used to live as a string array inside each WAF configuration. That works for one route and stops working at two: the same baseline gets copied, the copies drift, and a fix lands in one of them.

A WafRuleset is that body of rules with a name on it. A configuration lists the rulesets it wants, in order, and adds whatever is specific to itself on top.

Threat Protection → WAF rulesets.

The shape​

{
"id": "waf-ruleset_9c2f...",
"name": "Baseline CRS",
"description": "What every public route runs",
"enabled": true,
"rules": [
"@import_preset crs",
"SecRuleEngine On"
]
}

Then a configuration references it:

{
"name": "public-api",
"rulesets": ["waf-ruleset_9c2f..."],
"rules": [
"SecRuleRemoveById 942100"
]
}

Order is the whole of it​

The rules that reach the engine are the referenced rulesets, in the order they are listed, then the configuration's own inline rules.

rulesets: ["baseline", "api-hardening"] ──▶ baseline's rules
api-hardening's rules
rules: ["SecRuleRemoveById 942100"] ──▶ the config's own

That order is not cosmetic. @import_preset crs has to be read before anything that refers to what it defines, and an exclusion has to come after the rule it excludes — which is exactly why inline rules land last. "A shared baseline, plus this route's exceptions" is the arrangement the ordering is designed for.

Nothing is forced​

A configuration that carries its rules inline and references no ruleset behaves exactly as it always did. There is no migration to run, and no flag day: adopt rulesets on the configurations where sharing actually helps, leave the rest alone.

A reference to nothing still compiles

A ruleset id that does not resolve — deleted, or mistyped — contributes no rules. The configuration still compiles and the route still serves traffic; it simply protects less than it says it does.

Three things make that visible rather than silent:

  • the Compile button reports Compiled, but these rulesets do not exist: …;
  • the gateway logs a warning on every state sync naming the config and the missing ids;
  • a disabled ruleset is reported the same way, so switching one off is never mistaken for deleting it.

None of them stop the route. Refusing to serve traffic because a reference broke would turn a configuration mistake into an outage, which is the wrong trade for a component whose job is to keep requests flowing.

What it unlocks​

Composition is a prerequisite rather than an end in itself. It is what lets a tuning assistant write an exclusion into a named place instead of appending to a blob, and what gives curated rule packs a unit to ship as.

Editing a ruleset takes effect immediately​

Configurations are recomposed whenever a ruleset is written, so a rule added to a shared ruleset reaches every configuration referencing it without any of them being touched. The composed result is computed once per change, not once per request.