Back to Article List

CrowdSec AppSec on nginx: An open source WAF setup

CrowdSec AppSec on nginx: An open source WAF setup

Updated 22 October 2026

The AppSec component turns CrowdSec into a web application firewall. Without it, CrowdSec reads your logs and bans an address after the web server has already answered the request. With it, the nginx bouncer forwards each request to a local CrowdSec listener before nginx passes it on, and a request that matches a rule gets a 403 without your application ever seeing it.

This guide sets it up on Ubuntu 24.04 with nginx and CrowdSec 1.7 or 1.8 (LumaDock's template ships 1.7.8, and everything up to the bot detection section works on it), following the AppSec quickstart as the documentation describes it in October 2026. It installs the in-band rules first, proves them with one request, then adds the OWASP Core Rule Set in a mode that can't break your site on day one.

Prerequisites and the nginx bouncer

You need a running Security Engine; the CrowdSec install guide for Debian 12 and 13 gets you there, and its commands work unchanged on Ubuntu 24.04. You also need nginx itself; the bouncer package depends on it, so apt pulls it in if it's missing. Then add the bouncer:

sudo apt update
sudo apt install crowdsec-nginx-bouncer

Don't skip the update on a freshly deployed server. On a new LumaDock template VPS the package lists date from the image build, so without it apt installs nginx bouncer 1.1.6: fine for everything in this guide, too old for bot detection.

It's a Lua module that runs in nginx's access phase. It checks each client against the ban decisions from the local API and, once AppSec is switched on, sends the request to the AppSec listener for inspection. The package registers the bouncer with the local API during install and prints the bouncer's API key while it does, so keep that output out of tickets and chat logs.

It's a bigger install than the name suggests. On Ubuntu 24.04 apt pulls in a compiler toolchain and luarocks, about 185 MB on disk, because the package builds two Lua libraries from luarocks.org during setup; the server needs outbound HTTPS at that point. LumaDock's nginx + CrowdSec template deploys with nginx and CrowdSec preinstalled but without this bouncer, so the step still applies there. dpkg -l crowdsec-nginx-bouncer answering "no packages found" confirms it.

Install the AppSec rule collections

sudo cscli collections install \
    crowdsecurity/appsec-virtual-patching \
    crowdsecurity/appsec-generic-rules

appsec-virtual-patching carries rules for known CVEs, which the hub updates daily, plus rules for the constant probing for exposed .env files. appsec-generic-rules adds rules for common attack patterns that aren't tied to a single CVE. The install also pulls the default AppSec configuration, crowdsecurity/appsec-default, which loads both the virtual patching rules and the generic rules in-band: they're evaluated before the request continues, and a match blocks it. On 1.7.x every hub command also opens with a "new CrowdSec release is available" warning, which you can ignore here.

Both sets match narrow, specific patterns, which keeps false positives rare. That's what makes in-band blocking safe to leave on for them.

Turn on the AppSec listener

Rules do nothing until an acquisition starts the listener. Create /etc/crowdsec/acquis.d/appsec.yaml:

appsec_configs:
  - crowdsecurity/appsec-default
labels:
  type: appsec
listen_addr: 127.0.0.1:7422
source: appsec
sudo systemctl restart crowdsec

Keep listen_addr on 127.0.0.1. Only nginx on the same machine should ever talk to it, and the CrowdSec docs are explicit that the component must not be reachable from the internet.

Point the nginx bouncer at port 7422

The bouncer config ships with an empty APPSEC_URL= line, so one sed fills it in:

sudo sed -i 's|^APPSEC_URL=.*|APPSEC_URL=http://127.0.0.1:7422|' \
    /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf
sudo systemctl restart nginx

grep of the nginx bouncer config showing APPSEC_URL set to http://127.0.0.1:7422 and APPSEC_FAILURE_ACTION passthrough

After the sed: APPSEC_URL points at port 7422 and the failure action stays passthrough.

Two other settings in /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf deserve a decision now rather than during an incident:

  • APPSEC_FAILURE_ACTION decides what nginx does when the AppSec listener can't be reached. The default, passthrough, lets the request through uninspected. I keep that on anything customers use, because a stopped CrowdSec service taking the whole site down with it is the worse outage.
  • APPSEC_DROP_UNREADABLE_BODY is false by default. The library the bouncer relies on can't read the body of an HTTP/2 or HTTP/3 request that arrives without a Content-Length, so those bodies go uninspected. Setting it to true drops such requests, which closes the gap and can break clients that stream uploads.

Local overrides can go in crowdsec-nginx-bouncer.conf.local, which takes precedence over the main file. It won't make upgrades silent, though. The installer writes your API key into the main file, so dpkg treats that file as locally modified and asks about it whenever a bouncer upgrade ships a changed version. Answer N, the default, to keep yours.

Test the WAF with a .env probe

Give CrowdSec half a minute after the restart first. On a 2-vCPU test VPS the AppSec listener took up to 35 seconds to come back, and until it does the bouncer passes requests through uninspected, so a test sent too early looks like a broken setup. sudo ss -ltn | grep 7422 prints a line once the listener is up.

curl -I http://localhost/.env

Expect HTTP/1.1 403 Forbidden. The request matched crowdsecurity/vpatch-env-access, the AppSec listener answered 403 and the bouncer served CrowdSec's ban page; your application never saw it. Confirm the count:

curl -I http://localhost/.env returning HTTP/1.1 403 Forbidden from nginx with CrowdSec AppSec

The .env probe gets a 403: the WAF answered before the application saw the request.

sudo cscli metrics show appsec

cscli metrics show appsec with Processed and Blocked counts and the vpatch-env-access rule that fired

Processed and Blocked count the probe, and vpatch-env-access is the rule that fired.

The output has a Processed and a Blocked column per listener and a table of the rules that fired. On 1.8 an empty Bot Detection table sits above them until you set up bot detection. If Processed stays at zero while you send traffic, the bouncer isn't forwarding: check APPSEC_URL and that nginx was restarted after the change.

Two pages to keep at hand once real traffic flows through it. When a user reports a full-page 403, that's a decision to find and lift, covered in fixing the CrowdSec Access Forbidden page. And if switching on enforcement on a production server makes you nervous, the CrowdSec safe rollout guide covers the order of allowlists and bouncers that avoids locking yourself out.

Your idea deserves better hosting

24/7 support 30-day money-back guaranteeRefund covers the VPS plan on your first order. Cancel anytime
مدة الإشتراك

VPS.S1

£4.46 Save  17 %
£3.72 شهري
  • 2 vCPU AMD EPYC
  • 2 GB RAMذاكرة
  • 30 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول

VPS.S3

£11.17 Save  33 %
£7.44 شهري
  • 4 vCPU AMD EPYC
  • 6 GB RAMذاكرة
  • 70 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول

VPS.S4

£14.90 Save  20 %
£11.92 شهري
  • 6 vCPU AMD EPYC
  • 8 GB RAMالذاكرة
  • 80 GB NVMeالتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول

EPYC VPS.P1

£6.70 Save  22 %
£5.21 شهري
  • 2 vCPU AMD EPYC
  • 4 GB RAMذاكرة
  • 40 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

EPYC VPS.P2

£12.66 Save  24 %
£9.68 شهري
  • 2 vCPU AMD EPYC
  • 8 GB RAMذاكرة
  • 80 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

EPYC VPS.P4

£22.35 Save  23 %
£17.13 شهري
  • 4 vCPU AMD EPYC
  • 16 GB RAMذاكرة
  • 160 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

EPYC VPS.P5

£29.80 Save  25 %
£22.35 شهري
  • 8 vCPU AMD EPYC
  • 16 GB RAMذاكرة
  • 180 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

EPYC VPS.P6

£44.70 Save  25 %
£33.53 شهري
  • 8 vCPU AMD EPYC
  • 32 GB RAMذاكرة
  • 200 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

EPYC VPS.P7

£52.16 Save  29 %
£37.25 شهري
  • 16 vCPU AMD EPYC
  • 32 GB RAMذاكرة
  • 240 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

Genoa VPS.G2

£18.62 Save  20 %
£14.90 شهري
  • 2 vCPUمعالج AMD EPYC Genoa من الجيل الرابع 9xx4 بسرعة 3.25 GHz أو ما يماثله، على معمارية Zen 4. AMD EPYC G4
  • 4 GB DDR5ذاكرة
  • 50 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

Genoa VPS.G4

£33.53 Save  22 %
£26.07 شهري
  • 4 vCPUمعالج AMD EPYC مع أنوية vCPU مخصصة، على عتاد خوادم للمؤسسات. AMD EPYC G4
  • 8 GB DDR5ذاكرة
  • 100 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

Genoa VPS.G6

£67.06 Save  22 %
£52.16 شهري
  • 8 vCPUمعالج AMD EPYC مع أنوية vCPU مخصصة، على عتاد خوادم للمؤسسات. AMD EPYC G4
  • 16 GB DDR5ذاكرة
  • 200 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

Genoa VPS.G7

£119.22 Save  22 %
£93.14 شهري
  • 8 vCPUمعالج AMD EPYC مع أنوية vCPU مخصصة، على عتاد خوادم للمؤسسات. AMD EPYC G4
  • 32 GB DDR5ذاكرة
  • 250 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

AMD Ryzen VPS.R1

£12.66 Save  18 %
£10.43 شهري
  • 1 CPU مخصص معالج AMD Ryzen 9 7950X بسرعة 4.5 GHz أو ما يماثله، على معمارية Zen 4. vCPU
  • 4 GB DDR5ذاكرة
  • 50 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6 مشمول دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا.
  • نسخ احتياطي تلقائي مشمول

AMD Ryzen VPS.R2

£22.35 Save  17 %
£18.62 شهري
  • 2 CPU مخصصان معالج AMD Ryzen 9 7950X بسرعة 4.5 GHz أو ما يماثله، على معمارية Zen 4. vCPU
  • 8 GB DDR5ذاكرة
  • 100 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6 مشمول دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا.
  • نسخ احتياطي تلقائي مشمول

AMD Ryzen VPS.R4

£81.96 Save  18 %
£67.06 شهري
  • 8 CPU مخصصة معالج AMD Ryzen 9 7950X بسرعة 4.5 GHz أو ما يماثله، على معمارية Zen 4. vCPU
  • 32 GB DDR5ذاكرة
  • 400 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6 مشمول دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا.
  • نسخ احتياطي تلقائي مشمول

*VAT excluded.

INCLUDED WITH EVERY PLAN

✓ No setup fees ✓1 Gbps network
✓ Full root access on KVM ✓ Free server monitoring ✓ Firewall management ✓24/7 support

Add the OWASP Core Rule Set out-of-band

The OWASP Core Rule Set (CRS) is a generic ruleset, and generic rules produce false positives: a support form where someone pastes an SQL snippet, a base64 blob in a cookie, an API that accepts HTML. CrowdSec runs CRS through the Coraza engine and ships it in two modes, and the documentation recommends starting with the non-blocking one.

sudo cscli collections install crowdsecurity/appsec-crs

Add its config to the acquisition file next to the default:

appsec_configs:
  - crowdsecurity/appsec-default
  - crowdsecurity/crs

Restart CrowdSec. In this mode CRS runs out-of-band: the rules are evaluated asynchronously and can't block a request, but every match still produces an event. The collection also installs the crowdsecurity/crowdsec-appsec-outofband scenario, which bans an address that triggers out-of-band rules on more than five requests in quick succession. An attacker gets a handful of free probes and then loses access to everything the bouncers protect.

The current crowdsecurity/crs config on the hub raises an alert for every out-of-band match, so sudo cscli alerts list doubles as the list of what CRS would have blocked. (The CRS installation page still describes an older version without those alerts and shows how to add them; on a current hub that step isn't needed.) I'd run it that way for a few weeks before moving anything to blocking mode, and then only on routes that never carry user-written content, such as an admin panel or a login form. Blocking mode is a separate collection, crowdsecurity/appsec-crs-inband, loaded as crowdsecurity/crs-inband.

cscli alerts list showing an out-of-band CRS anomaly score alert for a SQL injection probe that was not blocked

CRS out-of-band: the SQL injection probe went through, but it raised an anomaly-score alert.

Keep virtual patches current for new CVEs

Virtual patching rules arrive through the hub, separately from engine releases. When the React Server Components flaw CVE-2025-55182, known as React2Shell, went public on 3 December 2025, the hub received vpatch-CVE-2025-55182, and CrowdSec's React2Shell writeup describes how that response played out. A server picks such rules up with:

sudo cscli hub update
sudo cscli hub upgrade
sudo systemctl reload crowdsec

Put it on a schedule. If the server hosts WordPress, the appsec-wordpress collection adds virtual patches for the most exploited WordPress vulnerabilities, plugins included.

Add bot detection to the same WAF

CrowdSec 1.8.0 added bot detection to AppSec. It answers a question the rules above don't ask: is the visitor a real browser at all? Visitors solve a small proof-of-work and send a device fingerprint, stock headless browsers and automation frameworks are refused a cookie, and verified crawlers such as Googlebot pass without a challenge. It needs nginx bouncer 1.2.0 or newer and its own collection, and the setup, including the Content-Security-Policy conflict that can freeze the challenge page, is in how to set up CrowdSec bot detection on nginx. The feature is still labelled alpha. Still on 1.7.x? Upgrade first, because the collection doesn't exist for older engines:

sudo apt update
sudo apt install --only-upgrade crowdsec crowdsec-nginx-bouncer

cscli on CrowdSec 1.7.8 warning a new release exists and unable to find the appsec-bot-challenge collection

On 1.7.8 the bot detection collection doesn't exist yet: upgrade first.

Resource cost and the limits of virtual patching

AppSec adds a hop: every request that isn't already banned makes a loopback round trip to port 7422 before nginx passes it on. The in-band rule set is small and the CRS work in out-of-band mode happens off the request path. I haven't benchmarked the overhead on a busy server myself, so if you push serious traffic through one machine, measure response times before and after the way you would for any middleware.

A virtual patch also stops being enough the day the real fix ships. It matches the exploit patterns rule authors know about, and attackers look for encodings and paths those rules don't cover. It buys the time between disclosure and your maintenance window; the vulnerable code stays vulnerable until you update it.

Questions?

Is ModSecurity end of life?

No, but its long-time maintainer stepped away. Trustwave handed custodianship of ModSecurity to OWASP in January 2024 and stopped supporting it on 1 July 2024, and F5 ended its NGINX ModSecurity WAF product on 31 March 2024. CrowdSec AppSec doesn't depend on ModSecurity at all: it runs the Core Rule Set on Coraza, an OWASP WAF engine written in Go.

Ellie Grace Hayes
About the author

Ellie Grace Hayes

I grew up in Minnesota and started working remotely before I decided that travelling was going to become a permanent part of my life. And it kinda did, more or less... I tend to stay somewhere for a month or two, sometimes longer if I like the place (so "digital nomad" is technically accurate, even if most days are much less exciting than the term suggests). Usually I'm at a desk in a rented room trying to work out why a command behaved differently on two supposedly identical machines :)

I came into technical writing through support work & community tutorials. Linux was the first subject I wrote about, but since then I've wandered into containers, hosting, networking plus a lot of self-hosted software. I've played Minecraft since I was a kid and still go through phases where I get completely absorbed in it for a few weeks. I don't have a five-year plan. I've tried - it keeps becoming a six-month plan instead.

Deploy your next project in seconds

Bring your winning ideas online faster, with modern hardware and unmetered bandwidth. Join a European cloud trusted by thousands of developers and businesses worldwide.