How to Install and Configure ModSecurity with NGINX
Complete guide to deploying ModSecurity 3.x with NGINX for free, open-source WAF protection using the OWASP Core Rule Set.
ModSecurity is the industry-standard open-source WAF, protecting millions of websites worldwide. Combined with NGINX, it provides powerful, customizable protection against OWASP Top 10 vulnerabilities, SQL injection, cross-site scripting, and countless other web attacks—all at zero licensing cost.
This guide walks you through installing ModSecurity 3.x (libmodsecurity) with the NGINX connector, configuring the OWASP Core Rule Set, and tuning the setup for production use. By the end, you'll have enterprise-grade WAF protection running on your NGINX server.
Prerequisites
- NGINX installed and running (open source or Plus)
- Root or sudo access to your server
- Ubuntu 20.04+, Debian 11+, or RHEL/CentOS 8+ (other distros may vary)
- Basic familiarity with NGINX configuration
- Git installed for cloning repositories
Step-by-Step Guide
Install ModSecurity 3.x Dependencies
ModSecurity 3.x (libmodsecurity) requires several dependencies. Install them based on your distribution:
Ubuntu/Debian:
sudo apt update
sudo apt install -y apt-utils autoconf automake build-essential \
git libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre2-dev \
libtool libxml2-dev libyajl-dev pkgconf wget zlib1g-dev
RHEL/CentOS/Rocky:
sudo dnf install -y gcc-c++ flex bison yajl curl-devel zlib-devel \
pcre2-devel autoconf automake git curl make libxml2-devel \
pkgconfig libtool httpd-devel lmdb-devel GeoIP-devel
Clone and Build ModSecurity 3.x
Clone the ModSecurity repository and build the library:
cd /opt
sudo git clone --depth 1 -b v3/master --single-branch https://github.com/owasp-modsecurity/ModSecurity
cd ModSecurity
# Initialize and update submodules
sudo git submodule init
sudo git submodule update
# Build and install
sudo ./build.sh
sudo ./configure
sudo make
sudo make install
Clone and Build the NGINX Connector
The NGINX ModSecurity connector integrates libmodsecurity with NGINX as a dynamic module:
# Clone the connector
cd /opt
sudo git clone --depth 1 https://github.com/owasp-modsecurity/ModSecurity-nginx.git
# Get your NGINX version
nginx -v
# Download matching NGINX source (replace version as needed)
wget http://nginx.org/download/nginx-1.24.0.tar.gz
tar -xzf nginx-1.24.0.tar.gz
cd nginx-1.24.0
# Build the dynamic module
./configure --with-compat --add-dynamic-module=../ModSecurity-nginx
make modules
# Copy module to NGINX modules directory
sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules/
Enable the ModSecurity Module in NGINX
Load the ModSecurity module in your NGINX configuration:
Add this line at the very top of /etc/nginx/nginx.conf (before the events block):
load_module modules/ngx_http_modsecurity_module.so;
Test the configuration:
sudo nginx -t
You should see: syntax is ok and test is successful.
Create ModSecurity Configuration
Set up the ModSecurity configuration directory and base configuration:
# Create directory structure
sudo mkdir -p /etc/nginx/modsec
# Copy the recommended configuration
sudo cp /opt/ModSecurity/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf
# Copy the Unicode mapping file
sudo cp /opt/ModSecurity/unicode.mapping /etc/nginx/modsec/
Edit /etc/nginx/modsec/modsecurity.conf and change the engine from detection to blocking:
# Change this line:
SecRuleEngine DetectionOnly
# To (when ready for production):
SecRuleEngine On
Install OWASP Core Rule Set (CRS)
The OWASP Core Rule Set provides comprehensive protection against common attacks:
cd /etc/nginx/modsec
sudo git clone https://github.com/coreruleset/coreruleset.git
cd coreruleset
# Create configuration from example
sudo cp crs-setup.conf.example crs-setup.conf
Create a main rules file at /etc/nginx/modsec/main.conf:
Include /etc/nginx/modsec/modsecurity.conf
Include /etc/nginx/modsec/coreruleset/crs-setup.conf
Include /etc/nginx/modsec/coreruleset/rules/*.conf
Enable ModSecurity in NGINX Server Blocks
Enable ModSecurity for your sites by adding directives to your server blocks:
server {
listen 80;
server_name example.com;
# Enable ModSecurity
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
location / {
proxy_pass http://backend;
# ... other directives
}
}
Reload NGINX to apply changes:
sudo nginx -t && sudo systemctl reload nginx
Test the WAF
Verify ModSecurity is working by sending a test attack:
# This should be blocked (SQL injection attempt)
curl -I 'http://your-server.com/?id=1%20OR%201=1'
# This should be blocked (XSS attempt)
curl -I 'http://your-server.com/?q=<script>alert(1)</script>'
Check the ModSecurity audit log:
sudo tail -f /var/log/modsec_audit.log
You should see entries showing the blocked requests with rule IDs and attack details.
Configure Logging
Configure ModSecurity logging in /etc/nginx/modsec/modsecurity.conf:
# Audit log settings
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/modsec_audit.log
# Debug log (disable in production)
SecDebugLog /var/log/modsec_debug.log
SecDebugLogLevel 0
Create the log files with correct permissions:
sudo touch /var/log/modsec_audit.log
sudo chown www-data:www-data /var/log/modsec_audit.log
Tune for False Positives
After running in DetectionOnly mode, review logs for false positives. Common tuning approaches:
Exclude specific rules:
# In /etc/nginx/modsec/crs-setup.conf or a custom file:
SecRuleRemoveById 942100 # Disable specific rule
SecRuleRemoveByTag "OWASP_CRS/WEB_ATTACK/SQL_INJECTION" # Disable by tag
Exclude rules for specific URLs:
SecRule REQUEST_URI "@beginsWith /api/upload" \
"id:1000,phase:1,pass,nolog,\
ctl:ruleRemoveById=200002"
Adjust paranoia level:
In crs-setup.conf, set the paranoia level (1=low false positives, 4=maximum security):
SecAction "id:900000,phase:1,pass,t:none,\
setvar:tx.blocking_paranoia_level=1"
Conclusion & Next Steps
Your NGINX server is now protected by ModSecurity with the OWASP Core Rule Set. You have enterprise-grade WAF protection at zero licensing cost.
Next steps:
- Monitor
/var/log/modsec_audit.logregularly for blocked attacks and false positives - After 1-2 weeks of DetectionOnly mode, switch SecRuleEngine to On
- Set up log rotation for the audit log
- Consider integrating logs with your SIEM (ELK, Splunk, etc.)
- Schedule regular CRS updates (monthly recommended)
- Document any custom rule exclusions for your team
Troubleshooting
Module fails to load: undefined symbol
This means the NGINX connector was compiled against a different NGINX version. Recompile the module using the exact NGINX source version matching your installed NGINX (check with nginx -v).
Legitimate requests being blocked
Check /var/log/modsec_audit.log for the rule ID causing the block. Either create an exclusion rule or adjust the paranoia level. Common culprits: file uploads, rich text editors, JSON APIs.
No logs appearing in audit log
Verify the log file exists and has write permissions for the NGINX user (www-data or nginx). Check that SecAuditEngine is not set to Off.
Performance degradation
ModSecurity adds some latency (typically 1-5ms). If you see significant slowdown: reduce the number of active rules, lower the request body limit (SecRequestBodyLimit), or consider caching more aggressively in NGINX.
make fails during ModSecurity build
Usually a missing dependency. Review error messages carefully. On Ubuntu, ensure you have the -dev versions of packages (e.g., libcurl4-openssl-dev, not just libcurl4).
Frequently Asked Questions
Should I use ModSecurity 2.x or 3.x with NGINX?
Use ModSecurity 3.x (libmodsecurity). Version 2.x was designed for Apache and only works with NGINX through cumbersome workarounds. Version 3.x was rebuilt as a standalone library with a native NGINX connector, offering better performance and compatibility.
How does ModSecurity impact NGINX performance?
ModSecurity typically adds 1-5ms latency per request, depending on rule complexity and request size. For most applications, this is negligible. High-traffic sites should benchmark and consider adjusting SecRequestBodyLimit and paranoia levels to optimize performance.
Can I use ModSecurity with NGINX Plus?
Yes, the same ModSecurity connector works with NGINX Plus. However, NGINX Plus users might also consider NGINX App Protect, F5's commercial WAF that's specifically optimized for NGINX Plus with additional features and support.
How do I update the OWASP Core Rule Set?
Navigate to the coreruleset directory and pull the latest changes: cd /etc/nginx/modsec/coreruleset && sudo git pull. Then reload NGINX. Consider testing updates in a staging environment first, as new rules may cause unexpected blocks.
What's the difference between DetectionOnly and On?
DetectionOnly logs potential attacks but doesn't block them—requests still reach your application. On mode actively blocks malicious requests. Always start with DetectionOnly to identify false positives before switching to On in production.
Can I use ModSecurity to protect multiple sites on one NGINX server?
Yes. You can enable ModSecurity globally in the http block for all sites, or selectively per server block. You can also use different rule configurations per site by specifying different modsecurity_rules_file paths in each server block.
Related Guides
How to Set Up Cloudflare WAF for WordPress
Step-by-step guide to configuring Cloudflare Web Application Firewall to protect your WordPress site from attacks.
WAF Security Best Practices Guide
Essential best practices for configuring and maintaining your Web Application Firewall for optimal security.