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.

45-60 minutes intermediate 10 steps
Last updated: Jul 18, 2026

ModSecurity is a widely deployed open-source WAF engine that has protected millions of websites. Combined with NGINX, it provides customizable protection against OWASP Top 10 vulnerabilities, SQL injection, cross-site scripting, and many 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 a fully working WAF running on your NGINX server.

Before you start, know the project status: Trustwave's commercial sponsorship of ModSecurity ended on July 1, 2024, and the engine is now an OWASP-maintained project in maintenance mode (security and bug fixes, with a slower feature pace). ModSecurity 3.x remains fully functional and is still packaged for major distributions, so this guide stays valid. If you are choosing a WAF engine today, also evaluate Coraza, the actively developed, Go-based, SecLang/CRS-compatible successor; see our Coraza vs ModSecurity comparison.

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

1

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
Tip: These dependencies are required to compile ModSecurity from source. Package managers don't always ship the latest version.
2

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
Tip: Using --depth 1 reduces download size significantly by only fetching the latest commit.
Warning: The build process takes 10-15 minutes depending on your server's resources. Don't interrupt it.
3

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 the NGINX source that EXACTLY matches your installed version.
# Replace 1.30.4 below with the version reported by 'nginx -v'
# (1.30.4 is the current stable release at the time of writing).
wget http://nginx.org/download/nginx-1.30.4.tar.gz
tar -xzf nginx-1.30.4.tar.gz
cd nginx-1.30.4

# 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/
Warning: The NGINX source version MUST match your installed NGINX version exactly. Check with 'nginx -v' and download the matching tarball from nginx.org.
4

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.

Tip: If you get module load errors, verify the .so file exists and has correct permissions (644).
5

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/

The recommended file ships with SecRuleEngine DetectionOnly. Edit /etc/nginx/modsec/modsecurity.conf and change the engine to blocking only after you have tuned out false positives:

# Change this line:
SecRuleEngine DetectionOnly

# To (when ready for production):
SecRuleEngine On
Warning: Keep SecRuleEngine DetectionOnly initially to test without blocking legitimate traffic. Switch to On after tuning.
6

Install OWASP Core Rule Set (CRS)

The OWASP Core Rule Set provides comprehensive protection against common attacks. The CRS project recommends installing a supported, tagged release rather than tracking the main development branch:

cd /etc/nginx/modsec

# Clone a specific supported release tag. Check the releases page for the
# latest version: https://github.com/coreruleset/coreruleset/releases
sudo git clone --branch v4.28.0 --depth 1 https://github.com/coreruleset/coreruleset.git
cd coreruleset

# Create the CRS setup file from the example
sudo cp crs-setup.conf.example crs-setup.conf

# Enable the exclusion templates. The rules/*.conf glob does NOT pick up
# the two exclusion files, which ship only as .example, so copy them to
# .conf or your custom before/after-CRS exclusions will never load.
sudo cp rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example \
  rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
sudo cp rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example \
  rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.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
Tip: Pin a tagged CRS release and update by checking out a newer tag rather than pulling the main branch, so production only ever sees reviewed, released rules.
7

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
Tip: You can enable ModSecurity globally in the http block or selectively per server/location block.
8

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.

Warning: If requests aren't being blocked, verify SecRuleEngine is set to On and NGINX was reloaded.
9

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
Tip: Set SecDebugLogLevel to 3-9 temporarily when troubleshooting rule issues, but keep it at 0 in production.
10

Tune for False Positives

After running in DetectionOnly mode, review logs for false positives. Common tuning approaches:

Exclude specific rules:

# In /etc/nginx/modsec/coreruleset/crs-setup.conf or a custom file:
SecRuleRemoveById 942100  # Disable one specific rule by ID
SecRuleRemoveByTag "attack-sqli"  # Disable all SQLi rules by tag (CRS 4)

In CRS 3 and 4 the SQLi tag is attack-sqli (or OWASP_CRS/ATTACK-SQLI). The old CRS 2.x tag OWASP_CRS/WEB_ATTACK/SQL_INJECTION no longer exists and matches zero rules, so double-check tag names against the rule files before relying on an exclusion.

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"
Tip: Start with paranoia level 1 and increase gradually as you tune out false positives for your application.

Conclusion & Next Steps

Your NGINX server is now protected by ModSecurity with the OWASP Core Rule Set, at zero licensing cost.

Next steps:

  • Monitor /var/log/modsec_audit.log regularly 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.)
  • Update the CRS by checking out newer supported release tags (monthly review recommended)
  • Document any custom rule exclusions for your team
  • Since ModSecurity is now an OWASP maintenance-mode project, evaluate Coraza for new or greenfield deployments; it is CRS-compatible and actively developed

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. Note that F5's older NGINX ModSecurity WAF reached end of life on March 31, 2024, so App Protect is the current commercial path.

How do I update the OWASP Core Rule Set?

Navigate to the coreruleset directory and check out a newer supported release tag, then reload NGINX: cd /etc/nginx/modsec/coreruleset && sudo git fetch --tags && sudo git checkout v4.28.0 (replace with the latest tag from the releases page). Avoid tracking the main branch with git pull, as it contains in-progress rule changes that can cause unexpected blocks. Test updates in a staging environment first.

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.

Is ModSecurity still maintained, and should I consider an alternative?

ModSecurity is still maintained, but its status changed. Trustwave's commercial sponsorship ended on July 1, 2024, and the engine moved to OWASP as a maintenance-mode project (security and bug fixes, slower feature development). It remains fully functional and packaged for major distributions. For new deployments, evaluate Coraza, an OWASP, Go-based engine that speaks SecLang and runs the same OWASP Core Rule Set, and is positioned as the actively developed successor.

Related Guides