Configure ModSecurity 3 web application firewall with OWASP Core Rule Set for advanced threat protection

Intermediate 45 min Apr 03, 2026 3,506 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Set up ModSecurity 3 with OWASP Core Rule Set 4.0 to protect web applications from SQL injection, XSS, and other attacks. Configure integration with Nginx and Apache, implement custom rules, and optimize performance for production environments.

Prerequisites

  • Root or sudo access
  • Web server (Nginx or Apache) installed
  • Basic understanding of web server configuration
  • At least 2GB RAM for compilation

What this solves

ModSecurity 3 is an open-source web application firewall (WAF) that protects your web applications from common attacks like SQL injection, cross-site scripting (XSS), and remote file inclusion. Combined with the OWASP Core Rule Set (CRS), it provides enterprise-grade threat detection and blocking capabilities. This tutorial shows you how to install ModSecurity 3, configure it with OWASP CRS 4.0, integrate it with popular web servers, and optimize performance for production use.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest security patches and dependencies.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install build dependencies

Install the required development tools and libraries needed to compile ModSecurity 3 from source.

sudo apt install -y build-essential git cmake libpcre2-dev libxml2-dev libcurl4-openssl-dev libyajl-dev libgeoip-dev liblua5.3-dev libfuzzy-dev libpcre3-dev zlib1g-dev
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y git cmake pcre2-devel libxml2-devel libcurl-devel yajl-devel GeoIP-devel lua-devel ssdeep-devel pcre-devel zlib-devel

Download and compile ModSecurity 3

Clone the ModSecurity repository and compile the library with optimizations enabled for production use.

cd /opt
sudo git clone --depth 1 -b v3.0.12 https://github.com/owasp-modsecurity/ModSecurity.git
cd ModSecurity
sudo git submodule init
sudo git submodule update
sudo ./build.sh
sudo ./configure --with-pcre2 --with-yajl --with-geoip --with-lua --enable-parser-generation
sudo make -j$(nproc)
sudo make install

Configure library path

Add ModSecurity libraries to the system library path so web servers can find them at runtime.

echo '/usr/local/modsecurity/lib' | sudo tee /etc/ld.so.conf.d/modsecurity.conf
sudo ldconfig

Download OWASP Core Rule Set

Download the latest OWASP CRS 4.0 which provides comprehensive protection rules against common web attacks.

cd /opt
sudo wget https://github.com/coreruleset/coreruleset/archive/v4.0.0.tar.gz
sudo tar -xzf v4.0.0.tar.gz
sudo mv coreruleset-4.0.0 /etc/modsecurity
sudo chown -R root:root /etc/modsecurity
sudo chmod -R 755 /etc/modsecurity

Create ModSecurity configuration

Set up the main ModSecurity configuration with logging, audit settings, and security policies.

# ModSecurity Configuration
SecRuleEngine On
SecRequestBodyAccess On
SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "^application/json" "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
SecRequestBodyInMemoryLimit 131072
SecRequestBodyLimitAction Reject
SecRule REQBODY_ERROR "!@eq 0" "id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'Error %{reqbody_error_msg}',severity:2"
SecRule MULTIPART_STRICT_ERROR "!@eq 0" "id:'200003',phase:2,t:none,log,deny,status:400, msg:'Multipart request body failed strict validation'"
SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" "id:'200004',phase:2,t:none,log,deny,status:44"
SecRule TX:/^MSC_/ "!@streq 0" "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain text/html text/xml
SecResponseBodyLimit 524288
SecResponseBodyLimitAction ProcessPartial
SecTmpDir /tmp/
SecDataDir /tmp/
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/modsecurity/audit.log
SecArgumentSeparator &
SecCookieFormat 0
SecUnicodeMapFile unicode.mapping 20127
SecStatusEngine On

Configure OWASP Core Rule Set

Create the CRS setup configuration with custom settings for your environment and security requirements.

# OWASP CRS 4.0 Setup Configuration
Include /etc/modsecurity/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example
SecDefaultAction "phase:1,log,auditlog,pass"
SecDefaultAction "phase:2,log,auditlog,pass"
SecAction "id:900000,phase:1,nolog,pass,t:none,setvar:tx.paranoia_level=1"
SecAction "id:900001,phase:1,nolog,pass,t:none,setvar:tx.executing_paranoia_level=1"
SecAction "id:900010,phase:1,nolog,pass,t:none,setvar:tx.inbound_anomaly_score_threshold=5"
SecAction "id:900011,phase:1,nolog,pass,t:none,setvar:tx.outbound_anomaly_score_threshold=4"
SecAction "id:900020,phase:1,nolog,pass,t:none,setvar:tx.allowed_methods=GET HEAD POST OPTIONS"
SecAction "id:900021,phase:1,nolog,pass,t:none,setvar:tx.allowed_request_content_type=|application/x-www-form-urlencoded| |multipart/form-data| |text/xml| |application/xml| |application/soap+xml| |application/x-amf| |application/json| |application/octet-stream| |application/csp-report| |application/xss-auditor-report| |text/plain|"
SecAction "id:900022,phase:1,nolog,pass,t:none,setvar:tx.allowed_http_versions=HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0"
SecAction "id:900023,phase:1,nolog,pass,t:none,setvar:tx.restricted_extensions=.asa/ .asax/ .ascx/ .axd/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .resources/ .resx/ .sql/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/"
SecAction "id:900024,phase:1,nolog,pass,t:none,setvar:tx.restricted_headers=/proxy-connection/ /lock-token/ /content-range/ /translate/ /if/"
SecAction "id:900030,phase:1,nolog,pass,t:none,setvar:tx.crs_validate_utf8_encoding=1"
SecAction "id:900031,phase:1,nolog,pass,t:none,setvar:tx.arg_name_length=100"
SecAction "id:900032,phase:1,nolog,pass,t:none,setvar:tx.arg_length=400"
SecAction "id:900033,phase:1,nolog,pass,t:none,setvar:tx.total_arg_length=64000"
SecAction "id:900034,phase:1,nolog,pass,t:none,setvar:tx.max_num_args=255"

Create log directory

Set up the directory structure for ModSecurity logs with appropriate permissions for the web server.

sudo mkdir -p /var/log/modsecurity
sudo chown www-data:www-data /var/log/modsecurity
sudo chmod 755 /var/log/modsecurity
Never use chmod 777. It gives every user on the system full access to your files. The web server user (www-data) needs write access to create log files, while maintaining security.

Nginx integration

Compile Nginx ModSecurity connector

Download and compile the Nginx connector module that enables ModSecurity integration with Nginx.

cd /opt
sudo git clone --depth 1 https://github.com/owasp-modsecurity/ModSecurity-nginx.git

Recompile Nginx with ModSecurity module

Get Nginx source and recompile it with the ModSecurity module enabled for dynamic loading.

sudo apt install -y nginx-dev
nginx_version=$(nginx -v 2>&1 | grep -o '[0-9.]\+')
cd /opt
sudo wget http://nginx.org/download/nginx-${nginx_version}.tar.gz
sudo tar -xzf nginx-${nginx_version}.tar.gz
cd nginx-${nginx_version}
sudo ./configure --with-compat --add-dynamic-module=../ModSecurity-nginx
sudo make modules
sudo cp objs/ngx_http_modsecurity_module.so /usr/lib/nginx/modules/
sudo dnf install -y nginx-mod-devel
nginx_version=$(nginx -v 2>&1 | grep -o '[0-9.]\+')
cd /opt
sudo wget http://nginx.org/download/nginx-${nginx_version}.tar.gz
sudo tar -xzf nginx-${nginx_version}.tar.gz
cd nginx-${nginx_version}
sudo ./configure --with-compat --add-dynamic-module=../ModSecurity-nginx
sudo make modules
sudo cp objs/ngx_http_modsecurity_module.so /usr/lib64/nginx/modules/

Configure Nginx with ModSecurity

Update the Nginx configuration to load the ModSecurity module and enable WAF protection on your sites. This configuration builds on standard Nginx reverse proxy setups with additional security layers.

load_module modules/ngx_http_modsecurity_module.so;

user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    # ModSecurity
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsecurity.conf;
    
    # Logging
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    access_log /var/log/nginx/access.log main;
    
    # Basic settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Create Nginx ModSecurity rules file

Create the main rules file that Nginx will use to load ModSecurity configuration and OWASP CRS rules.

# Include ModSecurity configuration
Include /etc/modsecurity/modsecurity.conf

# Include OWASP CRS setup
Include /etc/modsecurity/crs-setup.conf

# Include OWASP CRS rules
Include /etc/modsecurity/rules/*.conf

Create symbolic links for CRS rules

Set up the rules directory structure and link the OWASP CRS rule files for easy management.

sudo mkdir -p /etc/modsecurity/rules
sudo ln -sf /etc/modsecurity/rules/*.conf /etc/modsecurity/rules/
for rule in /etc/modsecurity/rules/REQUEST-9*.conf; do
    sudo ln -sf "$rule" /etc/modsecurity/rules/
done
for rule in /etc/modsecurity/rules/RESPONSE-9*.conf; do
    sudo ln -sf "$rule" /etc/modsecurity/rules/
done

Apache integration

Install Apache ModSecurity module

For Apache integration, compile the mod_security2 module from source for optimal compatibility with ModSecurity 3.

sudo apt install -y apache2-dev
cd /opt
sudo git clone --depth 1 https://github.com/owasp-modsecurity/ModSecurity-apache.git
cd ModSecurity-apache
sudo ./autogen.sh
sudo ./configure --with-libmodsecurity=/usr/local/modsecurity/
sudo make
sudo make install
sudo dnf install -y httpd-devel
cd /opt
sudo git clone --depth 1 https://github.com/owasp-modsecurity/ModSecurity-apache.git
cd ModSecurity-apache
sudo ./autogen.sh
sudo ./configure --with-libmodsecurity=/usr/local/modsecurity/
sudo make
sudo make install

Enable ModSecurity in Apache

Create Apache configuration to load and enable ModSecurity with OWASP CRS rules. This works alongside existing Apache virtual host configurations.

LoadModule security3_module /usr/lib/apache2/modules/mod_security3.so

Create Apache rules configuration

Set up the Apache-specific rules file that includes all ModSecurity and OWASP CRS configurations.

# Include ModSecurity configuration
Include /etc/modsecurity/modsecurity.conf

# Include OWASP CRS setup
Include /etc/modsecurity/crs-setup.conf

# Include OWASP CRS rules
Include /etc/modsecurity/rules/REQUEST-901-INITIALIZATION.conf
Include /etc/modsecurity/rules/REQUEST-903-IP-REPUTATION.conf
Include /etc/modsecurity/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
Include /etc/modsecurity/rules/REQUEST-910-IP-REPUTATION.conf
Include /etc/modsecurity/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
Include /etc/modsecurity/rules/REQUEST-912-DOS-PROTECTION.conf
Include /etc/modsecurity/rules/REQUEST-913-SCANNER-DETECTION.conf
Include /etc/modsecurity/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
Include /etc/modsecurity/rules/REQUEST-921-PROTOCOL-ATTACK.conf
Include /etc/modsecurity/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
Include /etc/modsecurity/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
Include /etc/modsecurity/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
Include /etc/modsecurity/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
Include /etc/modsecurity/rules/REQUEST-934-APPLICATION-ATTACK-NODEJS.conf
Include /etc/modsecurity/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
Include /etc/modsecurity/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
Include /etc/modsecurity/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
Include /etc/modsecurity/rules/REQUEST-944-APPLICATION-ATTACK-JAVA.conf
Include /etc/modsecurity/rules/REQUEST-949-BLOCKING-EVALUATION.conf
Include /etc/modsecurity/rules/RESPONSE-950-DATA-LEAKAGES.conf
Include /etc/modsecurity/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
Include /etc/modsecurity/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
Include /etc/modsecurity/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
Include /etc/modsecurity/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
Include /etc/modsecurity/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
Include /etc/modsecurity/rules/RESPONSE-980-CORRELATION.conf

Enable the module

Activate ModSecurity in Apache and restart the web server to apply the configuration.

sudo a2enmod modsecurity
sudo systemctl restart apache2
sudo systemctl status apache2
sudo systemctl restart httpd
sudo systemctl status httpd

Custom rules and whitelisting

Create custom rules file

Set up a custom rules file for application-specific security rules and exceptions that don't fit the standard OWASP CRS.

# Custom ModSecurity Rules

# Block common attack patterns
SecRule REQUEST_URI "@detectSQLi" \
    "id:1001,\
    phase:2,\
    block,\
    msg:'SQL Injection Attack Detected',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    tag:'attack-sqli',\
    severity:2"

# Block XSS attempts
SecRule ARGS "@detectXSS" \
    "id:1002,\
    phase:2,\
    block,\
    msg:'XSS Attack Detected',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    tag:'attack-xss',\
    severity:2"

# Rate limiting per IP
SecRule IP:RATE_LIMITED "@eq 1" \
    "id:1003,\
    phase:1,\
    deny,\
    status:429,\
    msg:'Rate limit exceeded',\
    expirevar:ip.rate_limited=60"

SecRule REQUEST_HEADERS:User-Agent "@streq \"\"" \
    "id:1004,\
    phase:1,\
    deny,\
    msg:'Empty User-Agent blocked'"

# Geographic IP blocking (example)
SecGeoLookupDB /etc/modsecurity/GeoLite2-Country.mmdb
SecRule GEO:COUNTRY_CODE "@streq CN" \
    "id:1005,\
    phase:1,\
    deny,\
    msg:'Access denied from country'"

Create whitelist exceptions

Configure rule exceptions for legitimate application functionality that might trigger false positives.

# ModSecurity Whitelist Rules - Application Exceptions

# Allow legitimate admin panel access
SecRule REQUEST_URI "@beginsWith /admin" \
    "id:2001,\
    phase:1,\
    pass,\
    ctl:ruleRemoveTargetById=942100;ARGS:query,\
    ctl:ruleRemoveTargetById=941160;ARGS:content"

# API endpoint exceptions
SecRule REQUEST_URI "@beginsWith /api/v1" \
    "id:2002,\
    phase:1,\
    pass,\
    ctl:ruleRemoveById=920230,\
    msg:'API endpoint - relax protocol enforcement'"

# Upload functionality exceptions
SecRule REQUEST_URI "@beginsWith /upload" \
    "id:2003,\
    phase:1,\
    pass,\
    ctl:ruleRemoveTargetById=200003;FILES,\
    msg:'File upload - allow multipart'"

# CMS-specific exceptions (WordPress example)
SecRule REQUEST_URI "@beginsWith /wp-admin" \
    "id:2004,\
    phase:1,\
    pass,\
    ctl:ruleRemoveTargetById=931130;ARGS:action,\
    ctl:ruleRemoveTargetById=942100;ARGS:post_content"

# Search functionality exceptions
SecRule ARGS:search "@unconditionalMatch" \
    "id:2005,\
    phase:1,\
    pass,\
    ctl:ruleRemoveTargetById=942100;ARGS:search,\
    msg:'Search query - allow SQL-lik

Automated install script

Run this to automate the entire setup

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. Fully managed, with one fixed contact who knows your setup.

You get one fixed contact who knows your setup

Rotterdam 01:29 · reachable in a message, no ticket form