Host runtime
Docker Desktop 29.2.0 on macOS, aarch64, 10 CPUs, 7.65 GiB assigned
Original WAFWiki lab
Original ModSecurity vs Coraza Docker comparison using the same OWASP CRS 4.25.0, upstream, and 20 HTTP decisions across clean, SQLi, XSS, query, and JSON cases.

Evidence policy
WAFWiki separates verified smoke-test results from architecture diagrams and methodology notes. A smoke test proves one narrow local path; it does not claim full production readiness.
Environment
Docker Desktop 29.2.0 on macOS, aarch64, 10 CPUs, 7.65 GiB assigned
OWASP CRS 4.25.0, blocking paranoia level 1, inbound anomaly threshold 5
ModSecurity 3.0.16, ModSecurity-nginx 1.0.4, NGINX image pinned by tag and digest
Coraza 3.7.0, coraza-caddy 2.5.0, Caddy 2.11.2
mccutchen/go-httpbin:2.21.0, image digest sha256:809250d14e94397f4729f617931068a9ea048231fc1a11c9e3c7cb8c28bbab8d
Ten cases per engine: six clean, two SQLi-like, and two XSS-like decisions
Commands
scripts/run-waf-evidence-lab.shThe script exits zero only when every observed status matches the expected status column.
awk -F, 'NR == 1 || $6 != $5 { print }' \
docs/lab-evidence/waf-engine-comparison-2026-07-26-requests.csvOnly the CSV header is printed because the final run contained no mismatch.
cat docs/lab-evidence/waf-engine-comparison-2026-07-26.jsonThe JSON records host details, component versions, image IDs, results, samples, resource snapshots, and limitations.
Observed result
Whole matrix
Both stacks produced the expected HTTP status for every declared case.
Clean decisions
Each engine allowed the same six representative clean inputs.
SQLi-like decisions
Query and JSON SQLi-like strings were blocked by both stacks with CRS 942100 evidence.
XSS-like decisions
Query and JSON script-tag strings were blocked by both stacks with CRS 941100 evidence.
Winner from this matrix
Matching outcomes on 20 requests do not prove equal coverage, false-positive rates, throughput, or operational fit.
shared rules: OWASP CRS 4.25.0
shared upstream: go-httpbin 2.21.0
ModSecurity: 10/10 expected statuses
Coraza: 10/10 expected statuses
clean: 12 HTTP 200 decisions across both stacks
controlled SQLi/XSS: 8 HTTP 403 decisions across both stacksEvidence context
OWASP CRS 4.25.0 was used on both paths. The test intentionally did not hide the remaining architectural difference: ModSecurity ran with NGINX while Coraza ran with Caddy.

Open evidence
The files expose the status matrix and versioned summary without publishing raw container logs or unrelated local data.
Twenty request outcomes with engine, case, method, path, expected status, observed status, and curl time.
Pinned image IDs, component versions, host details, request results, latency samples, resource snapshots, and limitations.
Limitations
Related research