Skip to content

Announcing Envoy AI Gateway 1.0: A Stable Foundation for Enterprise AI Traffic

Learn more

Get a High-Performance WAF With Better Rules: Benchmarks on Rules vs. Engine

We benchmarked three Envoy WAF flavors. The engine overhead is minimal — the cost is in the rules you load — and full OWASP CRS body inspection is production-viable with the right build.

Get a High-Performance WAF With Better Rules: Benchmarks on Rules vs. Engine

Every team that puts a Web Application Firewall in front of production traffic asks the same two questions: how much latency does it add, and how much hardware does it need? The answers floating around are usually anecdotes — measured on different proxies, different rule sets, and different payloads, so they can’t be compared or used for capacity planning.

We wanted real numbers, so we built a benchmark harness and measured three Envoy-based WAF flavors under identical conditions: same cluster, same pinned CPU cores, same traffic, same rule sets — including the full OWASP Core Rule Set (CRS). This post walks through the results and what they mean for sizing a WAF deployment. The full dataset is available for download at the end, so you can run your own analysis against your own traffic profile.

The headline findings:

  • The WAF engine itself is close to free — the cost is in the rules you load. With a minimal policy (a handful of header rules plus SQLi/XSS detection), every flavor we tested held 10,000+ RPS on 6 cores1 while adding under 2.5 ms of p99 latency on bodies up to 10 KB — and the dynamic-module builds stayed under 1 ms. That’s the engine’s floor, not a typical deployment.
  • Full OWASP CRS with request-body inspection is where implementations differentiate. The C-accelerated WAF that ships with Tetrate Enterprise Gateway for Envoy (TEG) sustained ~1,000 RPS per 6-core instance on 1 KB bodies with a 4.86 ms p992 — and it was the only flavor still sustaining that rate at 10 KB bodies.
  • Rule-set scope is the biggest performance lever you have — bigger than the choice of implementation. Matching the rule set to the traffic matters more than any other tuning knob.

The three flavors we benchmarked

All three run the same battle-tested Envoy proxy; two of them add a WAF based on Coraza, the ModSecurity-compatible WAF engine that Tetrate engineers help maintain as members of the project:

  • Vanilla Envoy — plain Envoy with no WAF filter. This is the baseline every other number is measured against.
  • Pure Go Coraza WAF — Coraza compiled as a pure-Go Envoy dynamic module, with no C dependencies. That keeps it portable and dependency-free to distribute — you can run it on any stock Envoy today.
  • Tetrate Enterprise Gateway for Envoy WAF (TEG WAF) — the same Coraza engine, built with C-accelerated matching: the RE2 regex engine and libinjection for SQLi/XSS detection. This is the enterprise-grade build that ships with TEG.
  • Coraza Golang Envoy filter — the community coraza-envoy-go-filter, which embeds Coraza through Envoy’s Go HTTP filter instead of a dynamic module. Like TEG WAF, it links the C-accelerated matchers (RE2 and libinjection).

How we measured

Benchmarks are only useful if the harness can’t lie to you, so the environment was built to remove every source of noise:

  • Dedicated, isolated hardware. A GKE cluster with a compute-optimized c3-standard-8 node (Sapphire Rapids) reserved exclusively for the system under test. Envoy ran as a single pod with 6 exclusively pinned vCPUs (Guaranteed QoS + static CPU manager), so no other workload could steal cycles. The load generator ran on a separate 22-vCPU node so it could never be the bottleneck, and all nodes shared one zone to keep network latency out of the measurement.
  • Coordinated-omission-safe load. Fortio drove constant target throughput across 64 connections with uniform request spacing, swept at 1,000 → 4,000 → 10,000 RPS to find each configuration’s knee.
  • Benign traffic through the full pipeline. Requests are inspected but not blocked, so every request traverses the complete WAF pipeline and reaches the backend. We’re measuring processing cost, not early-exit shortcuts.
  • Three rule-set tiers. Headers-only (phase-1 and phase-3 header rules, body access off), targeted rules (a typical custom policy: a handful of SecRules plus libinjection SQLi/XSS detection, with request body parsing on), and full OWASP CRS at default paranoia — the worst-case rule cost. The exact rules for each tier are listed below.
  • Four traffic shapes. GET with headers only, then JSON POST bodies of 1 KB, 10 KB, and 50 KB.

The exact rule sets

All three tiers are plain SecLang — the ModSecurity-compatible rule language Coraza speaks — passed verbatim to each WAF flavor, so every implementation evaluates identical policy.

Headers-only keeps body access off, so only phase-1 header rules ever run — a URI block and a scanner User-Agent match. This is the cheapest possible WAF mode:

SecRuleEngine On
SecRequestBodyAccess Off
SecResponseBodyAccess Off
SecRule REQUEST_URI "@contains /admin" "id:101,phase:1,t:lowercase,log,deny,status:403"
SecRule REQUEST_HEADERS:User-Agent "@pm grabber masscan nikto sqlmap" "id:9131,phase:1,t:none,log,deny,status:403"

Targeted rules (named simple-rules in the dataset) turns request-body parsing on with a bounded 12.5 MB limit, adds two custom SecRules, and runs the @detectXSS / @detectSQLi operators — backed by libinjection in the C-accelerated builds — against all request arguments:

SecRuleEngine On
SecRequestBodyAccess On
SecRequestBodyLimit 13107200
SecRequestBodyLimitAction ProcessPartial
SecResponseBodyAccess Off
SecRule REQUEST_URI "@contains /admin" "id:101,phase:1,t:lowercase,log,deny,status:403"
SecRule ARGS:payload "@streq maliciouspayload" "id:102,phase:2,t:lowercase,log,deny,status:403"
SecRule ARGS_NAMES|ARGS "@detectXSS" "id:9411,phase:2,t:none,t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls,log,deny,status:403"
SecRule ARGS_NAMES|ARGS "@detectSQLi" "id:9421,phase:2,t:none,t:utf8toUnicode,t:urlDecodeUni,t:removeNulls,multiMatch,log,deny,status:403"
SecRule REQUEST_HEADERS:User-Agent "@pm grabber masscan nikto sqlmap" "id:9131,phase:1,t:none,log,deny,status:403"

Full OWASP CRS loads the entire Core Rule Set at its default paranoia level, with the same body limits. The CRS rule files ship embedded in the WAF module (via coraza-coreruleset), so the whole policy is three includes:

Include @coraza.conf
SecRequestBodyLimit 13107200
SecRequestBodyLimitAction ProcessPartial
SecResponseBodyAccess Off
Include @crs-setup.conf
Include @owasp_crs/*.conf
Info

How to read the numbers:

  • A configuration is sustained when Envoy delivered ≥95% of the offered load with under 1% errors and CPU headroom to spare.
  • A ≥ value is a ceiling — the sweep topped out at 10,000 RPS without saturating, so the true maximum is higher.
  • A ~ value is saturated — the best throughput Envoy could deliver at that point, reported with the latency measured there.

Every run in the dataset completed with a 0% error rate; saturation showed up as throughput and latency knees, never as failures.

Result 1: the WAF engine is nearly free — the cost is in the rules

With the targeted rule set — request body parsing on, custom SecRules, and libinjection SQLi/XSS detection — all three WAF flavors rode the load generator’s 10,000 RPS ceiling1 on 6 cores3. This tier is deliberately minimal (five rules), so what it isolates is the engine’s fixed cost: body buffering, request parsing, and rule-phase execution, independent of any particular policy. Vanilla Envoy’s p99 at that load is about 1 ms; here is what each WAF added on top:

Two-panel grouped bar chart for the three WAF flavors under targeted rules, across GET and 1, 10, and 50 KB POST bodies. The top panel shows added p99 latency versus plain Envoy, ranging from 0 ms to 12.39 ms; the bottom panel shows peak memory, ranging from 36 MB to 76 MB. The Coraza Golang Envoy filter's 50 KB latency bar is hatched and starred because it was measured at a lower load than the rest.
Figure 1: Targeted rules + injection detection: added p99 latency vs vanilla Envoy (top) and peak memory (bottom), at each flavor's maximum sustained load. The starred, hatched bar was measured at a lower load — see the note below.
Note

The starred (*) bar deserves a closer read: the Coraza Golang Envoy filter’s +1.25 ms at 50 KB looks lower than TEG WAF’s +5 ms — and even lower than its own +2.42 ms at 10 KB — but it is not a like-for-like number.4 At equal load, TEG WAF and the Pure Go Coraza WAF are the flavors that held the 10,000 RPS ceiling on 50 KB bodies.

For header-only GETs and bodies up to 10 KB, the added p99 stays under 2.5 ms for every flavor — and for the two dynamic-module builds it stays under 1 ms. Even at 50 KB bodies, TEG WAF adds 5 ms at 10,000 RPS, and the Pure Go Coraza WAF adds 12.39 ms while still holding the same throughput ceiling. CPU cost is just as modest: 0.16–0.33 cores per 1,000 RPS on bodies up to 10 KB (rising to ~0.4–0.6 at 50 KB), with peak memory under 80 MB in every case.

This is worth pausing on. A pure-Go WAF with zero native dependencies — one you can drop into any stock Envoy — inspects request bodies with injection detection at 10,000 RPS while adding less than a millisecond of tail latency on typical payloads.

To be clear about what this tier represents: five rules is not a WAF posture. It fits scenarios where you’re hunting for specific patterns — a scanner User-Agent, a known injection vector, one sensitive path — rather than deploying broad protection. What the result proves is that the engine’s fixed cost is negligible on every build, so the performance budget of a real deployment is spent almost entirely on the rules you choose to load. The next section measures the other end of that spectrum: the entire Core Rule Set.

Result 2: full OWASP CRS is where builds differentiate

The full Core Rule Set runs hundreds of rules against every request, and once request bodies are in play the cost profile changes completely. This is the workload where implementation engineering — which regex engine, how body buffering is handled, how the rule phases execute — decides the outcome:

Three-panel chart for full OWASP CRS: the top panel shows maximum sustained RPS per WAF flavor across payload sizes, the middle panel shows p99 latency at that load, and the bottom panel shows peak memory, which grows from around 100 MB on header traffic to around 200 MB at 50 KB bodies. TEG WAF sustains 991 RPS at 1 KB with a 4.86 ms p99 and 964 RPS at 10 KB.
Figure 2: Full OWASP CRS: sustained throughput (top), p99 latency at that load (middle), and peak memory (bottom) per flavor and payload size. Hatched bars are saturated (best achieved).

On header-only traffic, all three flavors sustain ~3,900–4,000 RPS with the full CRS — and at moderate loads the tail latency is small (p99 of 2.5–3.6 ms at 1,000 RPS). The separation appears when bodies arrive:

PayloadPure Go Coraza WAFTEG WAFCoraza Golang Envoy filter
GET, headers only3,991 RPS · p99 26.74 ms3,978 RPS · p99 38.98 ms3,863 RPS · p99 44.63 ms
POST 1 KB~594 RPS (saturated5)991 RPS · p99 4.86 ms2991 RPS · p99 6.70 ms
POST 10 KB~63 RPS (saturated5)964 RPS · p99 177.55 ms~976 RPS (saturated5) · p99 240.50 ms
POST 50 KBnot run6~281 RPS (saturated)~263 RPS (saturated5)

Three things stand out:

  • TEG WAF is the only flavor that keeps sustaining ~1,000 RPS per instance as bodies grow to 10 KB. At 1 KB it pairs that throughput with a 4.86 ms p992 — full CRS inspection at single-digit-millisecond tail latency. RE2 and libinjection are doing exactly the job they were linked in for.
  • The Coraza Golang Envoy filter tracks TEG WAF closely at 1 KB (991 RPS, 6.70 ms p99). It links the same C-accelerated matchers as TEG WAF (RE2 and libinjection), yet it saturates one step earlier as payloads grow and carries a slightly higher fixed per-request cost, so TEG WAF still comes out ahead: the Envoy dynamic-module data path is the leaner integration.
  • The pure-Go build saturates early on this workload — by design, it’s built for a different job. Trading the C libraries away buys portability and zero-dependency distribution, which is the right trade for the scoped rule sets it excels at in Result 1. For full CRS on body-bearing traffic, reach for the C-accelerated build instead.

Full CRS also has a real resource footprint everywhere: peak memory grows from under 80 MB under targeted rules to 100–205 MB, and CPU cost at the knee ranges from 1.28 cores per 1,000 RPS at 1 KB bodies to 18.15 at 50 KB. These aren’t small numbers — they’re the honest price of running hundreds of rules against every request body, and they’re exactly what you need for capacity planning.

Sizing guidance you can take away

The numbers above are per single Envoy instance on 6 pinned cores; Envoy’s shared-nothing worker model means capacity scales out with replicas. From the measured data:

  • Targeted rules: budget ~0.2–0.6 cores per 1,000 RPS depending on payload size, and well under 100 MB of memory per instance. Latency impact is inside the noise of most services.
  • Full CRS on small bodies (≈1 KB): budget ~1.3 cores per 1,000 RPS on TEG WAF and expect a ~5 ms p99. A 6-core instance comfortably serves ~1,000 RPS.
  • Full CRS on larger bodies: cost grows steeply with payload size (~4 cores per 1,000 RPS at 10 KB). If your traffic carries large payloads, scope the rules that inspect bodies — or put the full CRS only on the routes that need it.
  • The cheapest optimization is policy, not hardware. The gap between targeted rules and full CRS is orders of magnitude larger than the gap between WAF implementations. The two tiers we measured are the endpoints of that spectrum: a production CRS deployment tuned for the backend it protects — excluding rule groups that don’t apply to your stack, and applying body inspection only on the routes that need it — lands somewhere in between. Start from what each route actually needs to inspect.

Which flavor for which workload

Your workloadReach for
Header rules, targeted custom SecRules, SQLi/XSS detectionAny flavor — including the portable, dependency-free Pure Go Coraza WAF provided by Built On Envoy
Full OWASP CRS on header-dominant trafficAny flavor sustains ~4,000 RPS per instance
Full OWASP CRS with request-body inspection at production throughputTEG WAF — the C-accelerated build is what keeps this workload sustained
Enterprise support, FIPS-ready builds, and a supported upgrade pathTEG — using the FIPS-validated images

Run your own analysis

Info

The complete dataset behind this post is available as a CSV download: summary.csv.
It contains one row per flavor × rule set × payload × offered load, with achieved throughput, the full latency distribution (avg, p50, p90, p99, p99.9), added latency vs the no-WAF baseline, error rate, CPU cores (average and peak), peak working-set memory, and CPU cost per 1,000 RPS. In the CSV, the flavors appear as baseline (vanilla Envoy), pure-go (Pure Go Coraza WAF), clibs (TEG WAF), and gofilter (Coraza Golang Envoy filter), and the targeted rule set appears as simple-rules.

Footnotes

  1. 10K RPS ceiling. The load sweep topped out at 10,000 RPS without saturating; figures described as ”≥” or as riding the ceiling are lower bounds, not measured maxima.
  2. 991 RPS at 4.86 ms p99. Measured on TEG WAF (clibs in the dataset) with full OWASP CRS on 1 KB POST bodies, at that flavor’s maximum sustained load on a single 6-core instance.
  3. Single instance. All results characterize one Envoy pod with 6 exclusively pinned vCPUs; multi-replica scaling was not measured, though Envoy’s shared-nothing worker model scales out horizontally.
  4. Unequal load on the starred bar. Every latency in the targeted-rules chart is reported at that flavor’s maximum sustained load. On 50 KB bodies the Coraza Golang Envoy filter could not sustain the 10,000 RPS step, so its bar is measured at 3,991 RPS while the other bars carry ≥9,990 RPS — shorter queues and a thinner tail at ~2.5× less load.
  5. Saturated (~) throughput. When a configuration could not sustain the offered load, we report the best achieved throughput with latency measured at that knee.
  6. Payloads above 50 KB. Not exercised in this benchmark run; do not extrapolate the Pure Go saturation curve beyond 10 KB.

Conclusion

The question “what does a WAF cost?” turns out to have a precise answer — it just depends on the rule set far more than on anything else:

  • The WAF engine adds almost nothing on its own: with a minimal targeted policy, every flavor held 10,000+ RPS on 6 cores with single-digit-millisecond added p99 — under 1 ms on typical payloads for the dynamic-module builds, including the portable Pure Go Coraza WAF. Your performance budget is spent on the rules, so size the rule set to the backend you’re protecting.
  • Full OWASP CRS with body inspection is a heavyweight workload, and the C-accelerated TEG WAF is what makes it sustainable at production throughput — ~1,000 RPS per 6-core instance with a 4.86 ms p99 at 1 KB bodies2, still sustained at 10 KB.
  • All of this runs on the same Envoy data plane you already operate, with the same Coraza engine Tetrate engineers help maintain upstream.

Tetrate Enterprise Gateway for Envoy packages the C-accelerated WAF together with enterprise support and FedRAMP-ready FIPS builds of Envoy Gateway. If you’re planning a WAF rollout and want help mapping these numbers to your traffic, talk to us — or grab the dataset and start with your own numbers.

This is the first in a blog series about WAF on Envoy Gateway. In our next blog we will share more complete benchmarks and details about per-request WAF cost.
Stay tuned!

Product background Product background for tablets
Building AI agents

Agent Router Enterprise provides a managed AI Gateway, MCP Gateway, and AI Guardrails in your dedicated instance. Graduate agents from prototype to production with consistent model access, governed tool use, and runtime supervision — built on Envoy AI Gateway by its creators.

  • AI Gateway – Unified model catalog with automatic fallback across providers
  • MCP Gateway – Curated tool access with per-profile authentication and filtering
  • AI Guardrails – Enforce policies, prevent data loss, and supervise agent behavior
  • Learn more
    Replacing NGINX Ingress

    Tetrate Enterprise Gateway for Envoy (TEG) is the enterprise-ready replacement for NGINX Ingress Controller. Built on Envoy Gateway and the Kubernetes Gateway API, TEG delivers advanced traffic management, security, and observability without vendor lock-in.

  • 100% upstream Envoy Gateway – CVE-protected builds
  • Kubernetes Gateway API native – Modern, portable, and extensible ingress
  • Enterprise-grade support – 24/7 production support from Envoy experts
  • Learn more
    Decorative CTA background pattern background background
    Tetrate logo in the CTA section Tetrate logo in the CTA section for mobile

    Ready to enhance your
    network

    with more
    intelligence?