CrowdSec AppSec + OpenResty: Modern WAF Without ModSecurity

After years running ModSecurity + OWASP CRS on nginx, I migrated arleo.eu to a more modern stack: CrowdSec AppSec on OpenResty. The result is a tighter inline WAF architecture — better integrated, easier to maintain, and fully coherent with the rest of the security stack.
Why Drop ModSecurity?
ModSecurity v2 is in maintenance mode. Managing OWASP CRS rules on classic nginx generates friction: frequent false positives, logs that are hard to correlate with CrowdSec, and a configuration spread across multiple tools with no unified view.
Moving to OpenResty (nginx + native LuaJIT) was already motivated by the CrowdSec bouncer, which runs on Lua. CrowdSec AppSec fits naturally into this architecture.
Architecture
Internet
↓
Cloudflare (CDN + cloud WAF + DDoS)
↓
OpenResty (NUC host)
↓
CrowdSec Lua bouncer
↙ ↘
IP check AppSec engine
(port 8080) (port 7422)
↓
OWASP CRS + custom rules
↓ (if OK)
Hugo VM (192.168.122.69)The Lua bouncer intercepts every request and submits it to two components in parallel:
- IP check (8080): classic CrowdSec decisions (bans, allowlist)
- AppSec engine (7422): inline WAF analysis — OWASP CRS, SQLi/XSS/path traversal detection
CrowdSec-whitelisted IPs bypass AppSec (ALWAYS_SEND_TO_APPSEC=false), avoiding unnecessary latency for trusted sources.
Key Bouncer Configuration
# /etc/crowdsec/bouncers/crowdsec-openresty-bouncer.conf
APPSEC_URL=http://127.0.0.1:7422
APPSEC_FAILURE_ACTION=passthrough
APPSEC_CONNECT_TIMEOUT=500
APPSEC_SEND_TIMEOUT=500
APPSEC_PROCESS_TIMEOUT=2000
ALWAYS_SEND_TO_APPSEC=falseAPPSEC_FAILURE_ACTION=passthrough is critical: if AppSec is unavailable (agent restart, load spike), traffic passes through anyway. No blackout because of a failing WAF component.
Exception for the MCP Endpoint
The MCP proxy (/mcp) carries payloads containing Markdown, bash, and ini code blocks — which push the OWASP CRS anomaly score up and trigger false positives. A whitelist rule disables 8 CRS IDs on this path only:
# /etc/crowdsec/appsec-rules/mcp-whitelist.yaml
name: crowdsecurity/mcp-whitelist
description: Disable CRS false positives on /mcp endpoint
type: appsec-rule
rules:
- zones: [METHOD, URI]
variables: [uri]
match:
type: startsWith
value: /mcp
actions:
- disable:932230
- disable:932235
- disable:932250
- disable:932340
- disable:941400
- disable:942360
- disable:949110
- disable:959100Advantages Over ModSecurity
Native integration — AppSec is a component of the CrowdSec agent. WAF decisions feed the same pipeline as IP bans: unified logs in BetterStack, metrics via cscli metrics show appsec, immediate correlation between application attacks and network behavior.
Simplified maintenance — OWASP CRS rules are managed via cscli hub update like any other CrowdSec collection. No more manual ModSecurity rule file management.
Resilience — passthrough on failure prevents cascading outages. ModSecurity in DetectionOnly mode required separate failover handling.
Performance — AppSec runs as a separate process from the CrowdSec agent. OpenResty + LuaJIT keeps latencies low, without the overhead of the ModSecurity Apache/nginx module.
Activation Scope
AppSec is only active on publicly-exposed vhosts:
www.arleo.eumcp-hugo.arleo.eu
Quick Verification
# Test AppSec directly
curl -s -X POST http://127.0.0.1:7422/ \
-H "x-crowdsec-appsec-uri: /.env" \
-H "x-crowdsec-appsec-ip: 1.2.3.4" \
-H "x-crowdsec-appsec-host: www.arleo.eu" \
-H "x-crowdsec-appsec-verb: GET"
# Expected: {"action":"ban","http_status":403}
# Metrics
cscli metrics show appsecConclusion
CrowdSec AppSec on OpenResty replaces ModSecurity cleanly, without technical debt. The CrowdSec ecosystem now covers the entire stack: behavioral detection (agent), IP blocking (Lua bouncer), inline WAF (AppSec), Cloudflare sync (crowdsec-cf-sync), and monitoring (BetterStack + Vector). A single source of truth for homelab security.

Resources
- 🛡️ CrowdSec — open source collaborative security platform
- ⚡ OpenResty — nginx + LuaJIT, high performance web server
- 📖 CrowdSec AppSec — Documentation — official installation and configuration guide
- 📖 CrowdSec AppSec Quickstart OpenResty — OpenResty bouncer quick start
- 📖 Protecting Web Applications with OWASP CRS and CrowdSec — official blog post
- 📖 OWASP CRS Installation with CrowdSec AppSec — CRS installation guide