ModSecurity WAF
Install and configure ModSecurity as a Web Application Firewall for Nginx, block SQLi, XSS, LFI and common web attacks
ModSecurity is an open-source Web Application Firewall (WAF) that inspects HTTP requests and blocks malicious traffic before it reaches your application. Combined with the OWASP Core Rule Set (CRS), it protects against SQLi, XSS, LFI, RFI, and hundreds of other attack patterns.
Requirements
- Nginx on Ubuntu/Debian
- 512 MB RAM minimum (CRS adds ~50 MB per worker)
Install ModSecurity
sudo apt update
sudo apt install libmodsecurity3 libmodsecurity-dev -yInstall the Nginx connector:
sudo apt install libnginx-mod-http-modsecurity -yEnable the module
sudo nano /etc/nginx/nginx.confAdd inside the http {} block (or verify it's auto-loaded):
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/main.conf;Configure ModSecurity
Copy the default config:
sudo mkdir -p /etc/nginx/modsecurity
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/nginx/modsecurity/modsecurity.confSet it to detection mode first (logs but doesn't block, safe for testing):
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/nginx/modsecurity/modsecurity.confCreate the main config file:
sudo nano /etc/nginx/modsecurity/main.confInclude /etc/nginx/modsecurity/modsecurity.conf
Include /etc/nginx/modsecurity/crs/crs-setup.conf
Include /etc/nginx/modsecurity/crs/rules/*.confInstall OWASP Core Rule Set (CRS)
The CRS provides 900+ rules against common attacks:
cd /tmp
wget https://github.com/coreruleset/coreruleset/archive/refs/tags/v4.7.0.tar.gz
tar -xzf v4.7.0.tar.gz
sudo mv coreruleset-4.7.0 /etc/nginx/modsecurity/crs
sudo cp /etc/nginx/modsecurity/crs/crs-setup.conf.example /etc/nginx/modsecurity/crs/crs-setup.confTest and apply
Test the Nginx config:
sudo nginx -tApply:
sudo systemctl reload nginxTest that ModSecurity is blocking attacks. Send a basic SQLi test:
curl -I "http://localhost/?id=1' OR '1'='1"You should get a 403 Forbidden. Check the audit log:
sudo tail -f /var/log/modsec_audit.logTuning: reduce false positives
CRS in paranoia level 1 (default) has few false positives. If you see legitimate requests being blocked, you can whitelist specific rules or paths.
Disable a specific rule:
# In your server {} block
modsecurity_rules '
SecRuleRemoveById 920350
';Whitelist a path:
location /admin {
modsecurity_rules '
SecRuleRemoveByTag "attack-sqli"
';
proxy_pass http://127.0.0.1:8080;
}Increase paranoia level (more rules, more false positives):
In crs-setup.conf:
SecAction \
"id:900000,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.paranoia_level=2"Levels: 1 (default) → 2 → 3 → 4 (most aggressive)
Per-virtualhost configuration
Enable only on specific sites:
server {
listen 80;
server_name example.com;
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/main.conf;
location / {
proxy_pass http://127.0.0.1:3000;
}
}Disable for a specific server block:
server {
server_name api.example.com;
modsecurity off;
}Useful log locations
| File | Description |
|---|---|
/var/log/modsec_audit.log | Full audit log of blocked requests |
/var/log/nginx/error.log | Nginx errors including ModSecurity denials |
Monitor blocked requests in real time:
sudo tail -f /var/log/modsec_audit.log | grep "Request\|URI\|Response"Start in detection-only mode (recommended for production migration)
If you're adding ModSecurity to an existing production site, start with detection only to avoid false positives:
sudo sed -i 's/SecRuleEngine On/SecRuleEngine DetectionOnly/' /etc/nginx/modsecurity/modsecurity.conf
sudo systemctl reload nginxMonitor logs for 24-48 hours, whitelist any false positives, then switch to blocking mode:
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/nginx/modsecurity/modsecurity.conf
sudo systemctl reload nginx