About
A Web Application Firewall (WAF) HTTP filter plugin that provides layer 7 security for web applications using the Coraza WAF engine.
Features
This plugin provides comprehensive web application security:
- OWASP CRS (Core Rule Set): Built-in protection against common web attacks
- Request inspection: Analyzes HTTP requests for malicious patterns and anomalies
- Response inspection: Examines HTTP responses to prevent data leakage
- Flexible rule engine: Supports custom security rules and policies
- Multiple operation modes: Detection-only or active blocking modes
- Detailed logging: Comprehensive audit logging of security events
Security Rules
The WAF comes with support for OWASP CRS, formerly known as ModSecurity Core Rule Set (CRS), as well as
some recommended configurations and allows custom rule configuration for specific security requirements.
It is recommended to review the provided configuration files before deploying, to ensure they align with
the target environment and the expected security posture.
To use the bundled rules, you can include them with the Coraza Include directives as shown in this example:
{
"directives": [
"Include @coraza.conf",
"Include @crs-setup.conf",
"Include @owasp_crs/*.conf"
]
}Available special names are:
| Name | Aliases | Description |
|---|---|---|
@coraza.conf-recommended |
— | Coraza upstream configuration file example |
@coraza.conf |
@recommended.conf, @recommended-conf |
Coraza configuration file tailored for Coraza dynamic module implementation |
@crs-setup.conf.example |
— | CRS upstream configuration file example |
@crs-setup.conf.example-nodefaultact |
— | CRS upstream configuration file example without the SecDefaultAction directives |
@crs-setup.conf |
@crs-setup-conf |
CRS configuration file tailored for Coraza dynamic module implementation |
@owasp_crs/ |
— | CRS rules directory |
Note: Aliases are provided for backward compatibility, but are deprecated and may be removed in future releases. It is recommended to use the primary names for new configurations.
To create custom rules, refer to the SecLang reference docs.
Mode
The optional mode setting controls which parts of the HTTP exchange are evaluated by the WAF.
Use the narrowest mode that matches your policy to avoid unnecessary processing.
REQUEST_ONLY(default): Inspect request headers, body, and trailers only. Best for deployments that focus on inbound only attack detection.FULL: Inspect both request and response phases. Best for a fully deployed WAF with OWASP CRS usage.RESPONSE_ONLY(deprecated): Inspect the response phase only, skipping request-phase rule evaluation. Retained for backward compatibility and will be removed in a future release. Note: response only mode is not compatibile with a default OWASP CRS installation because it skips request-phase initialization.
{
"mode": "FULL",
"directives": [
"Include @coraza.conf",
"Include @crs-setup.conf",
"Include @owasp_crs/*.conf"
]
}Note: mode only decides which request and response phases are passed to Coraza. It does not enable body access
by itself. For example, FULL allows response-phase evaluation, but response body inspection still requires
directives such as SecResponseBodyAccess On.
Request URI
The optional request_uri_path_only setting controls the value of REQUEST_URI (and REQUEST_LINE).
false(default): the absolute URL, e.g.https://example.com/api?x=1.true: the path and query, e.g./api?x=1, as in other Coraza and ModSecurity integrations. Path-based rules such asSecRule REQUEST_URI "@beginsWith /api/"require it.
Note: the default will change to true in a future release. We recommend enabling it now and updating
any rules that match the scheme or host in REQUEST_URI (use REQUEST_HEADERS:Host for the host).
Metadata
The plugin emits the following filter dynamic metadata when requests are blocked by the WAF:
| Namespace | Key | Description |
|---|---|---|
io.builtonenvoy.waf |
block_phase |
Phase in which the requests has been blocked. See the execution flow for details on the possible values. |
io.builtonenvoy.waf |
block_rule |
ID of the rule that triggered the block. |
Metrics
The extension exposes the following metrics:
| Metric | Tags | Type | Description |
|---|---|---|---|
waf_tx_total |
- | Counter | Total amount of transactions processed by the WAF. |
waf_tx_blocked |
authority, phase, rule_id |
Counter | Total amount of transactions blocked by the WAF. The rule_id will not be set if the request is not blocked by a concrete rule but by a WAF internal error. |
waf_tx_duration |
- | Histogram | Duration of WAF processing the transaction, in microseconds. |
Histogram resolution
Envoy records dynamic-module histograms without a unit, so its default bucket
bounds apply verbatim to the microsecond values of waf_tx_duration. Two
consequences:
- Typical CRS evaluation lands in the ~0.5–10 ms range, which the default
bounds cover with only five buckets (
500, 1000, 2500, 5000, 10000). - The largest finite bound is 3,600,000 µs (3.6 s).
_sum/_countand the admin-interface quantiles stay correct above it, but PromQL'shistogram_quantile()clamps any quantile falling past the last finite bound to that bound.
Both can be tuned in the Envoy bootstrap configuration, without touching the
extension. Two details about the matcher, both of which fail silently when
missed: it sees the stat name before tag extraction, i.e. with the
dynamicmodulescustom. prefix, and safe_regex is matched against the whole
name — hence the leading .* below.
stats_config:
histogram_bucket_settings:
- match:
safe_regex:
regex: ".*waf_tx_duration"
# Sharper resolution across typical CRS latencies; ceiling at 10 s.
buckets: [100, 250, 500, 750, 1000, 1500, 2500, 5000, 10000, 25000, 100000, 1000000, 10000000]Per Route Configuration
The extension supports per-route configuration, allowing you to specify different WAF rules and policies for different routes in your application. This is useful for tailoring security measures to specific endpoints or services.
The per-route configuration share the same format as the extension configuration and see Envoy's Dynamic Per Route Configuration for more details on how to set it up.
Usage Examples
SQL Injection Protection
The following example demonstrates how to run the WAF plugin with recommended OWASP CRS rules and test SQL injection protection by sending a malicious payload that should be blocked by the WAF.
By default WAF is configured in DetectionOnly mode, which means it will only log detected attacks without
blocking them. To enable active blocking, this example sets SecRuleEngine On in the configuration. It also
sets SecResponseBodyAccess Off to disable response body inspection as it's not needed for the example.
# Start WAF with recommended OWASP CRS rules and enforcement enabled
boe run --extension coraza-waf --config '
{
"directives": [
"Include @coraza.conf",
"SecRuleEngine On",
"SecResponseBodyAccess Off",
"Include @crs-setup.conf",
"Include @owasp_crs/*.conf"
]
}'
# Try sending a SQL injection payload and see the request rejected
curl -v http://localhost:10000/post -X POST --data "1%27%20ORDER%20BY%203--%2B"
< HTTP/1.1 403 Forbidden
< content-length: 22
< content-type: text/plain
< date: Thu, 12 Feb 2026 10:28:16 GMT
< server: envoy
<CRS plugin from local filesystem
The following example demonstrates how to load a CRS plugin from the local filesystem and run the WAF plugin with recommended OWASP CRS rules.
It first downloads the WordPress rule exclusions plugin to a local file, enables blocking with
SecRuleEngine On, and includes the plugin using Include /tmp/wordpress-plugin.conf. The same SQL injection
payload that would normally be blocked is no longer blocked because the plugin excludes this specific attack
vector.
# Retrieve the file
curl https://raw.githubusercontent.com/coreruleset/wordpress-rule-exclusions-plugin/refs/heads/master/plugins/wordpress-rule-exclusions-before.conf -o /tmp/wordpress-plugin-before.conf
# Start WAF with recommended OWASP CRS rules, enforcement enabled, and loading the plugin file
boe run --extension coraza-waf --config '
{
"directives": [
"Include @coraza.conf",
"SecRuleEngine On",
"SecResponseBodyAccess Off",
"Include @crs-setup.conf",
"Include /tmp/wordpress-plugin-before.conf",
"Include @owasp_crs/*.conf"
]
}'
# Try sending a SQL injection payload and see the request rejected
curl -v "http://localhost:10000/wp-json/wp/v2/posts/1" -X POST -H "Content-Type: application/json" --data '{"content": "SELECT * FROM users WHERE id=1"}'
# The request is no longer blocked by the WAF as the rules in the plugin file are excluding the specific attack vector from being detected
< HTTP/1.1 404 Not Found
< date: Wed, 01 Apr 2026 13:10:33 GMT
< content-type: text/html
< content-length: 233
< server: envoy
...