Security

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 -y

Install the Nginx connector:

sudo apt install libnginx-mod-http-modsecurity -y

Enable the module

sudo nano /etc/nginx/nginx.conf

Add 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.conf

Set 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.conf

Create the main config file:

sudo nano /etc/nginx/modsecurity/main.conf
Include /etc/nginx/modsecurity/modsecurity.conf
Include /etc/nginx/modsecurity/crs/crs-setup.conf
Include /etc/nginx/modsecurity/crs/rules/*.conf

Install 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.conf

Test and apply

Test the Nginx config:

sudo nginx -t

Apply:

sudo systemctl reload nginx

Test 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.log

Tuning: 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

FileDescription
/var/log/modsec_audit.logFull audit log of blocked requests
/var/log/nginx/error.logNginx errors including ModSecurity denials

Monitor blocked requests in real time:

sudo tail -f /var/log/modsec_audit.log | grep "Request\|URI\|Response"

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 nginx

Monitor 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

On this page