---
title: "CrowdSec AppSec + OpenResty: Modern WAF Without ModSecurity"
description: "How I replaced ModSecurity with CrowdSec AppSec on OpenResty to get a performant, well-maintained inline WAF fully integrated with the CrowdSec ecosystem."
url: "https://www.arleo.eu/en/posts/crowdsec-appsec-openresty/"
language: "en"
datePublished: "2026-05-18T00:07:55+02:00"
dateModified: "2026-05-18T05:44:50+02:00"
tags: ["crowdsec","nginx","security","waf","infrastructure","homelab"]
contentSignal: "ai-train=no, search=yes, ai-input=yes"
---

# CrowdSec AppSec + OpenResty: Modern WAF Without ModSecurity
How I replaced ModSecurity with CrowdSec AppSec on OpenResty to get a performant, well-maintained inline WAF fully integrated with the CrowdSec ecosystem.



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

```ini
# /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=false
```

`APPSEC_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:

```yaml
# /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:959100
```

## Advantages 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.eu`
- `mcp-hugo.arleo.eu`

## Quick Verification

```bash
# 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 appsec
```

## Conclusion

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.

---

![CrowdSec + OWASP + OpenResty](/images/logos/crowdsec-owasp-openresty.webp)

## Resources

- 🛡️ [**CrowdSec**](https://www.crowdsec.net/) — open source collaborative security platform
- ⚡ [**OpenResty**](https://openresty.org/) — nginx + LuaJIT, high performance web server
- 📖 [**CrowdSec AppSec — Documentation**](https://docs.crowdsec.net/docs/appsec/intro/) — official installation and configuration guide
- 📖 [**CrowdSec AppSec Quickstart OpenResty**](https://docs.crowdsec.net/docs/appsec/quickstart/nginxopenresty/) — OpenResty bouncer quick start
- 📖 [**Protecting Web Applications with OWASP CRS and CrowdSec**](https://www.crowdsec.net/blog/protecting-your-web-applications-with-owasp-crs-and-crowdsec) — official blog post
- 📖 [**OWASP CRS Installation with CrowdSec AppSec**](https://docs.crowdsec.net/docs/appsec/crs/installation) — CRS installation guide


## Tags

- crowdsec
- nginx
- security
- waf
- infrastructure
- homelab
