Skip to content

Set Up a Multi-Tier WAF with ModSecurity, Nginx, and CrowdSec

Layer free, open-source security tools to block brute-force attacks, bad bots, and application exploits before they reach customer sites.

Written by AISali·July 31, 2026·5 min read
Set Up a Multi-Tier WAF with ModSecurity, Nginx, and CrowdSec

Why a Single Layer of Defense Isn't Enough Anymore#

Shared hosting is a magnet for automated attacks. Credential-stuffing bots, vulnerability scanners, and DDoS-for-hire scripts hammer servers around the clock. A firewall blocks network floods but sees nothing inside HTTP payloads. ModSecurity inspects payloads but can't throttle brute-force login storms that never trigger a CRS rule. Neither tool alone stops a botnet that already knows your IP ranges from a leaked customer list.

This tutorial walks you through building a multi-tier WAF stack using three free, production-proven tools:

  • Nginx as the front-door reverse proxy with rate limiting and geo-blocking.
  • ModSecurity v3 (with the OWASP CoreRuleSet) as the application-layer WAF.
  • CrowdSec as a collaborative threat-intelligence engine that shares blocklists across your fleet.

The result is a layered defense where each tool handles what it does best, and CrowdSec automatically bans repeat offenders at the network level before they waste CPU cycles.


Prerequisites#

You need a Linux server (Ubuntu 22.04 or Debian 12 recommended) running Nginx 1.24+ and root or sudo access. If you're running a hosting control panel, make sure it uses Nginx as its web server rather than Apache — or front Apache with an Nginx reverse proxy.

bash
sudo apt update && sudo apt install -y nginx libnginx-mod-http-modsecurity curl gnupg2

Step 1: Enable and Configure ModSecurity v3#

1.1 — Activate the Module#

ModSecurity ships as an Nginx dynamic module on most distros. Create the engine config:

bash
sudo mkdir -p /etc/nginx/modsec
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf

Edit /etc/nginx/modsec/modsecurity.conf and change:

code
SecRuleEngine On
SecAuditLog /var/log/modsec_audit.log

1.2 — Install the OWASP CoreRuleSet (CRS)#

bash
cd /tmp
curl -sL https://github.com/coreruleset/coreruleset/archive/refs/tags/v4.0.tar.gz | tar xz
sudo mv coreruleset-4.0 /etc/nginx/modsec/crs
sudo cp /etc/nginx/modsec/crs/crs-setup.conf.example /etc/nginx/modsec/crs/crs-setup.conf

Create /etc/nginx/modsec/main.conf to chain them together:

code
Include /etc/nginx/modsec/modsecurity.conf
Include /etc/nginx/modsec/crs/crs-setup.conf
Include /etc/nginx/modsec/crs/rules/*.conf

1.3 — Wire It into Nginx#

Add these directives inside your http {} or server {} block:

nginx
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;

Reload Nginx:

bash
sudo nginx -t && sudo systemctl reload nginx

At this point ModSecurity is inspecting every HTTP request against ~180 CRS rules covering SQL injection, XSS, remote code execution, and protocol violations.


Step 2: Add Nginx Rate Limiting and Bot Filtering#

ModSecurity excels at payload inspection, but it doesn't throttle floods. Add rate limiting directly in Nginx.

2.1 — Define Rate Zones#

In http {}:

nginx
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;

2.2 — Apply to Sensitive Endpoints#

nginx
location ~ ^/(cpanel|whm|webmail|admin) {
    limit_req zone=login burst=10 nodelay;
    limit_conn addr 10;
    proxy_pass http://backend;
}

location / {
    limit_req zone=general burst=50 nodelay;
    proxy_pass http://backend;
}

2.3 — Block Bad User-Agents#

Create /etc/nginx/snippets/block-bots.conf:

nginx
if ($http_user_agent ~* (semrush|ahrefs|mj12bot|dotbot|petalbot|Bytespider)) {
    return 444;
}

Include it in your server block:

nginx
include /etc/nginx/snippets/block-bots.conf;

HTTP 444 drops the connection silently — no response body wasted on scrapers.


Step 3: Install CrowdSec for Collaborative Intelligence#

CrowdSec reads logs (Nginx, ModSecurity audit, SSH, etc.), detects attack patterns with its local agent, and bans IPs via a firewall bouncer. Its killer feature: you can opt into a community blocklist of millions of known malicious IPs.

3.1 — Install the Agent#

bash
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash
sudo apt install crowdsec

3.2 — Install the Nginx and ModSecurity Collections#

bash
sudo cscli collections install crowdsecurity/nginx
sudo cscli collections install crowdsecurity/modsecurity
sudo cscli hub update && sudo cscli hub upgrade

These collections include parsers for Nginx access logs and ModSecurity audit logs, plus scenarios for port scanning, credential brute-forcing, and web crawling.

3.3 — Install the Firewall Bouncer#

bash
sudo apt install crowdsec-firewall-bouncer-iptables
sudo systemctl enable --now crowdsec-firewall-bouncer

The bouncer automatically inserts iptables (or nftables) rules to DROP traffic from banned IPs.

3.4 — Enable the Community Blocklist#

bash
sudo cscli capi register
sudo cscli collections install crowdsecurity/blocklist-malicious
sudo systemctl restart crowdsec

You'll immediately start blocking tens of thousands of known-bad IPs with zero effort.


Step 4: Verify and Monitor#

Check CrowdSec Decisions#

bash
sudo cscli decisions list

You should see bans accumulating within minutes.

Test ModSecurity#

bash
curl -I "https://yourserver.com/?id=1' OR '1'='1"

You should get a 403 Forbidden — that's CRS rule 942100 (SQL Injection Detection) doing its job.

Tail the Logs#

bash
sudo tail -f /var/log/modsec_audit.log
sudo cscli metrics

Keeping It Maintainable#

  • Tune false positives. CRS ships in blocking mode. Run it in detection-only mode (SecRuleEngine DetectionOnly) for a week on a busy server, review /var/log/modsec_audit.log, and add rule exclusions for legitimate traffic before switching to blocking.
  • Pin CrowdSec to your control panel logs. If you're running cPanel, DirectAdmin, or Salieno Core, add parsers for their access logs so brute-force login attempts get banned at the firewall level — not just by the panel's built-in rate limiter.
  • Automate CRS updates. Add a cron job to pull the latest CRS release monthly:
bash
0 3 1 * * cd /etc/nginx/modsec/crs && curl -sL https://github.com/coreruleset/coreruleset/archive/refs/tags/v4.0.tar.gz | tar xz --strip-components=1 && nginx -t && systemctl reload nginx

The Layered Payoff#

ThreatHandled By
SQLi, XSS, RCE payloadsModSecurity + CRS
Brute-force login floodsNginx rate limiting
Known malicious IPsCrowdSec community blocklist
Repeat offenders across your fleetCrowdSec shared decisions
Scrapers and bad botsNginx User-Agent filter

Each layer is free, open-source, and battle-tested in production. Together they give your shared hosting environment a defense posture that rivals commercial WAF appliances — without the per-domain licensing costs that eat into reseller margins. Start with ModSecurity and CRS today, add CrowdSec tomorrow, and you'll have a stack that scales with your fleet.

Share

0 comments

Loading comments…

More from the blog