How to Install ModSecurity, Nginx, OWASP CRS with Ubuntu 22.04-20.04

In the digital era, web application security is a paramount concern. Your organization’s data integrity, availability, and confidentiality are at stake. Thus, the need for robust security measures cannot be overstated. One such comprehensive, open-source solution is ModSecurity.

ModSecurity is a powerful, cross-platform web application firewall (WAF) providing an array of features to help protect your web applications from various threats. It operates by inspecting traffic coming to your web application, identifying malicious behavior, and blocking suspicious requests based on predefined rulesets.

The most recent version, ModSecurity 3, has been designed specifically to enhance compatibility and simplify integration with various web servers, including NGINX.

Why Use ModSecurity 3 with NGINX on an Ubuntu Server?

Integrating ModSecurity 3 with NGINX on an Ubuntu server offers a slew of benefits:

  • Advanced Protection: ModSecurity 3 shields your web applications from a wide range of attacks, including SQL injection, Cross-Site Scripting (XSS), Local File Inclusion (LFI), and more. It offers robust protection against the OWASP Top 10 Web Application Security Risks.
  • Improved Compatibility: ModSecurity 3 is built to integrate seamlessly with NGINX, enhancing its security capabilities without hampering performance.
  • Custom Rule Support: ModSecurity allows for custom rulesets, enabling you to tailor your security to the specific needs of your web applications.
  • Detailed Logging and Monitoring: ModSecurity provides comprehensive logs of all HTTP transactions, making monitoring your traffic and identifying potential security threats easier.
  • Open Source and Community Supported: ModSecurity is an open-source project with a vibrant community providing regular updates, new features, and ongoing support. It allows for cost-effective, transparent, and adaptable security solutions.

With Ubuntu being one of the most popular choices for web servers due to its stability, security, and usability, it’s no surprise that many choose to integrate ModSecurity 3 with NGINX on an Ubuntu server.

The benefits extend beyond the individual components to include:

  • Stability and Security: Ubuntu servers are known for their stability and secure architecture, making them an ideal choice for hosting web applications.
  • Ease of Use: Ubuntu’s user-friendly design and extensive online resources make managing and maintaining your server easier.
  • Extensive Support: Ubuntu has a large, active community and long-term support releases, ensuring you have access to help and updates when you need them.

To sum it all together, using ModSecurity 3 with NGINX on an Ubuntu server provides an effective, customizable, and comprehensive security solution for your web applications. This guide will demonstrate how to install ModSecurity 3 with the OWASP Core Rule Set included on Ubuntu 22.04 Jammy Jellyfish or Ubuntu 20.04 Focal Fossa, laying a strong foundation for your web application security.

Section 1: Preinstallation Steps

Before we begin installing ModSecurity, it’s crucial to ensure that our server is prepared for the tasks ahead. This involves making sure our system is up-to-date, and that we are working with the latest version of Nginx, a high-performance web server and reverse proxy server. These initial steps ensure a smooth installation process, minimizing potential compatibility issues and security vulnerabilities.

Step 1: Updating Ubuntu

The first step towards a secure and efficient server is keeping it up-to-date. This ensures that all software packages have the latest security patches and performance improvements. Execute the following command to update your system:

sudo apt update && sudo apt upgrade 

This command first updates the package lists for upgrades (sudo apt update).

Step 2: Removing Pre-existing Nginx Installation

If you have a pre-existing Nginx installation, we recommend removing it and installing the latest version from a custom PPA maintained by Ondřej Surý. This version comes with additional dynamic modules, such as the Brotli module, for improved compression.

First, stop the current Nginx service with the following:

sudo systemctl stop nginx

Then, remove the existing Nginx installation with the following commands:

sudo apt purge nginx -y && sudo apt autoremove nginx -y

Here, the purge option completely removes the Nginx package and its configuration files. The autoremove command removes any packages that were automatically installed to satisfy dependencies for Nginx but are now no longer needed.

Step 3: Adding the Latest Nginx PPA

After removing the older Nginx service, it’s time to add a new, up-to-date PPA (Personal Package Archive) for Nginx. You can choose between a stable or mainline version. The mainline version is generally recommended as it includes the latest features and improvements.

To add the stable PPA, execute:

sudo add-apt-repository ppa:ondrej/nginx-stable -y

Or for the mainline PPA, use:

sudo add-apt-repository ppa:ondrej/nginx-mainline -y

Step 4: Updating the Packages Index

After importing the desired repository, updating your APT sources list is necessary. This ensures the system knows about the new packages in the added repository. Update your sources list with the following:

sudo apt update

Now, install Nginx with the following command:

sudo apt install nginx

During the installation, you may be prompted to keep or replace your existing /etc/nginx/nginx.conf configuration file. It’s generally recommended to keep your current configuration file by pressing n.

Step 5: Adding Nginx Source Code to Repository

By default, the Nginx source code isn’t included when installing from the PPA. To compile Modsecurity later in this tutorial, you’ll need to enable this feature to download the Nginx source code manually.

Open the configuration file located in /etc/apt/sources.list.d:

sudo nano /etc/apt/sources.list.d/ondrej-ubuntu-nginx-mainline-*.list

Find the line that starts with # deb-src and uncomment it (i.e., remove the #). If you use a different third-party repository, replace the path in the command with the appropriate one:

# deb-src jammy main

Example of what it should look like:

Once done, save the file by pressing CTRL+O and then exit by pressing CTRL+X.

If you’re more comfortable with command-line utilities, you can also use the sed command to uncomment the source line:

sudo sed -i 's/# deb-src/deb-src/g' /etc/apt/sources.list.d/ondrej-ubuntu-nginx-mainline-*.list

Finally, update the repository list using the following command:

sudo apt update

This command ensures that your system acknowledges the addition of the Nginx source code to the repository, making it available for further operations.

Section 2: Acquiring the Nginx Source Code

To seamlessly integrate ModSecurity with Nginx, we’ll need to compile the ModSecurity dynamic module against the Nginx source code. To do this, we first need to download and store the Nginx source code in a specific directory (/usr/local/src/nginx). This section walks you through creating the necessary directories, installing dependencies, downloading the source code, and verifying that the source version matches the installed Nginx version.

Step 1: Setting Up the Directory Structure

Firstly, let’s create the directory where we will download and store the Nginx source code. Here’s the command to create the directory and navigate into it:

sudo mkdir /usr/local/src/nginx && cd /usr/local/src/nginx

This command creates the directory /usr/local/src/nginx and then changes the current directory to this newly created directory.

Step 2: Installing Dependencies and Downloading the Source Code

Before we can download the source code, we need to install a package called dpkg-dev. This package provides several development tools required to unpack, build, and upload Debian source packages. Install it with the following command:

sudo apt install dpkg-dev -y

With the necessary tools in place, we’re ready to download the Nginx source code. Use the following command to accomplish this:

sudo apt source nginx

While running this command, you might encounter an error message. Don’t worry; this error message can be safely ignored.

Step 3: Verifying the Source Version

After downloading the source code, it’s essential to ensure that the downloaded source version matches the installed Nginx version. Differences between these versions can lead to compatibility issues, so this verification step is crucial.

First, list the files in the current directory to confirm that the source code has been downloaded:


You should see several files and directories related to Nginx in the output.

Next, confirm that the source package version matches the Nginx version installed on your Ubuntu system. You can check the installed Nginx version with the following command:

The output will display your installed Nginx version. Ensure this version matches the source version you’ve downloaded. If they don’t match, you’ll need to download the correct version of the Nginx source code.

nginx -v

The output will display your installed Nginx version. Ensure this version matches the source version you’ve downloaded. If they don’t match, you’ll need to download the correct version of the Nginx source code.

Section 3: Installing libmodsecurity3 for ModSecurity

ModSecurity is an effective firewall for web applications, and libmodsecurity3 is its core component that handles HTTP filtering. In this section, we’ll guide you through obtaining and installing this essential software package.

Step 1: Cloning the ModSecurity Repository

To start, we need to fetch the source code for libmodsecurity3 from its GitHub repository. We’ll use Git for this, a widely-used distributed version control system. If Git isn’t already installed on your system, you can add it with the following command:

sudo apt install git

Before we clone the repository, it’s a good practice to adjust the ownership of the directory to avoid the need for root privileges (sudo) for most commands:

sudo chown -R $user:$user /usr/local/src/

his command recursively changes the ownership of /usr/local/src/ directory to the current user.

Next, let’s clone the libmodsecurity3 repository. We’ll use a shallow clone (--depth 1) to save bandwidth and storage. This means we’ll only fetch the latest revision of the v3/master branch:

git clone --depth 1 -b v3/master --single-branch /usr/local/src/ModSecurity/

After cloning the repository, change your working directory to the newly cloned repository:

cd /usr/local/src/ModSecurity/

Step 2: Installing libmodsecurity3 Dependencies

The compilation of libmodsecurity3 requires several dependencies. Install them with the following command:

sudo apt install gcc make build-essential autoconf automake libtool libcurl4-openssl-dev liblua5.3-dev libfuzzy-dev ssdeep gettext pkg-config libpcre3 libpcre3-dev libxml2 libxml2-dev libcurl4 libgeoip-dev libyajl-dev doxygen libpcre++-dev libpcre2-16-0 libpcre2-dev libpcre2-posix3 -y

These dependencies are necessary to compile Nginx with ModSecurity from version 1.21.4 onwards successfully. This step has been known to cause issues, but installing the correct dependencies should resolve this for nearly all circumstances.

Next, initialize and update the Git submodules with the following commands:

git submodule init
git submodule update

Step 3: Building the ModSecurity Environment

Now that we’ve prepared our workspace, it’s time to build the ModSecurity environment. First, run the build script:


Then, configure the environment:


During this process, you may encounter an error stating fatal: No names found, cannot describe anything. Don’t worry this error message can be safely ignored.

Step 4: Compiling the ModSecurity Source Code

Having built and configured the environment for libmodsecurity3, we’re ready to compile it. Here’s the command to do so:


For those working on high-performance servers, you can speed up the compilation process by running make with the -j option followed by the number of CPU cores your server has. For instance, if your server has six CPU cores, you could use:

make -j 6

Step 5: Installing the Compiled Code

After compiling the source code, the last step is to install it:

sudo make install

This command installs libmodsecurity3 to /usr/local/modsecurity/, which is the directory you’ll reference later in this guide.

Section 4: Installing the ModSecurity-nginx Connector

In this section, we’ll guide you through the process of installing the ModSecurity-nginx connector. This vital component serves as the liaison between Nginx, a popular web server software, and ModSecurity, ensuring seamless communication between the two.

Step 1: Cloning the ModSecurity-nginx Repository

First off, we need to fetch the source code of the ModSecurity-nginx connector. We do this by cloning the repository from GitHub. Similar to the previous step of cloning the libmodsecurity3 repository, use the following command to clone the ModSecurity-nginx repository:

git clone --depth 1 /usr/local/src/ModSecurity-nginx/

This command fetches only the latest revision of the ModSecurity-nginx repository, saving bandwidth and storage.

Step 2: Installing ModSecurity-nginx Dependencies

Next, we need to navigate to the Nginx source directory. You can do this by executing the following cd command:

cd /usr/local/src/nginx/nginx-1.*.*

In the Nginx source directory, we’ll install the dependencies required for the ModSecurity-nginx connector. Run this command to fetch and install the necessary dependencies:

sudo apt build-dep nginx && sudo apt install uuid-dev

Step 3: Compiling the ModSecurity-nginx Connector

With the dependencies in place, we can proceed to compile the ModSecurity-nginx connector. For this, we need to run the configure command with the --with-compat flag and --add-dynamic-module option:

./configure --with-compat --add-dynamic-module=/usr/local/src/ModSecurity-nginx

The --with-compat flag ensures compatibility with various systems, while the --add-dynamic-module option specifies the location of the ModSecurity-nginx connector.

After configuring the environment, create the dynamic modules by running the make command:

make modules

At the end of the process, your terminal should see something similar:

Step 4: Moving the Dynamic Module

The final step in this process is moving the dynamic module to the appropriate directory. The make modules command creates a dynamic module at the location objs/ You need to move this module to the /etc/nginx/modules/ directory. Use the following command to accomplish this:

sudo cp objs/ /etc/nginx/modules/

It’s important to note that you can store the dynamic module anywhere, provided that you specify the full path when loading it.

Section 5: Setting Up the ModSecurity-nginx Connector with Nginx

Having successfully installed the ModSecurity-nginx connector, the next step is to load it into Nginx and configure it appropriately. This involves editing your Nginx configuration file located at /etc/nginx/nginx.conf, which will make ModSecurity operational with your Nginx web server.

Step 1: Enabling ModSecurity in nginx.conf

To start, you need to specify the location of the ModSecurity module in the nginx.conf file. To do this, open the nginx.conf file using a text editor. For this example, we’ll be using nano:

sudo nano /etc/nginx/nginx.conf

At the top of the file, add the following line:

load_module modules/;

This command instructs Nginx to load the ModSecurity module. If you stored the module in a different location, ensure to include the full path in this command.

Next, add the following code under the http {} section:

modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/modsec-config.conf;

The above commands enable ModSecurity and specify the location of the ModSecurity rules file. Save the changes (Ctrl+O) and exit the file (Ctrl+X).

Step 2: Creating and Configuring Directory and Files for ModSecurity

To store configuration files and future rules, you need to create a directory. The following command will create the /etc/nginx/modsec directory:

sudo mkdir /etc/nginx/modsec/

Next, copy the sample ModSecurity configuration file from the cloned Git directory to the directory you’ve just created:

sudo cp /usr/local/src/ModSecurity/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf

Now, let’s modify the modsecurity.conf file. By default, the ModSecurity rule engine operates in DetectionOnly mode, which detects malicious behavior but does not block it. To change this behavior, open the modsecurity.conf file:

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

Locate the line SecRuleEngine DetectionOnly and change it to:

SecRuleEngine On

Next, locate the SecAuditLogParts line:

# Log everything we know about a transaction.
SecAuditLogParts ABIJDEFHZ

The current configuration is not correct. Modify the line to the following:

SecAuditLogParts ABCEFHJKZ

Now, save (Ctrl+O) and exit (Ctrl+X) the file.

Step 3: Creating modsec-config.conf

The next step is to create a modsec-config.conf file. This file will include the modsecurity.conf file and other rule sets, such as the OWASP CRS, later on. Create and open the file with this command:

sudo nano /etc/nginx/modsec/modsec-config.conf

Once inside the file, add the following line:

Include /etc/nginx/modsec/modsecurity.conf

Save (Ctrl+O) and exit (Ctrl+X) the file.

Finally, copy ModSecurity’s unicode.mapping file using the cp command:

sudo cp /usr/local/src/ModSecurity/unicode.mapping /etc/nginx/modsec/

Step 4: Validating the Configuration

Before proceeding, it’s crucial to validate your configuration. Run the following command to perform a dry run of the Nginx service:

sudo nginx -t

If everything is set up correctly, you should see the following message:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

This output indicates that your Nginx configuration file’s syntax is correct, and the configuration test was successful. If you encounter any errors, revisit the previous steps to ensure that all commands were entered correctly and that all file paths are accurate.

Step 5: Applying the Configuration Changes

Once you’ve validated your configuration and ensured that there are no syntax errors, you need to restart the Nginx service for the changes to take effect. This is a critical step in making ModSecurity operational.

Use the systemctl command to restart the Nginx service:

sudo systemctl restart nginx

This command instructs your system to stop and then immediately start the Nginx service. With this, your Nginx service should now have the ModSecurity WAF enabled and operational.

Section 6: Deploying the OWASP Core Rule Set for ModSecurity

In this section, we will enhance the security of our web server by deploying the OWASP Core Rule Set (CRS) for ModSecurity. Although ModSecurity is a powerful tool, it doesn’t provide protection on its own. It needs a set of rules to function effectively.

The OWASP CRS is a widely accepted and respected set of rules for web application firewalls (WAFs). These rules act as a robust protective barrier against most emerging threats on the internet, identifying potential attacks and blocking them. It’s a fundamental resource that many similar systems base their rules on, and by adopting it, you’re already on the right path to safeguarding your server.

Step 1: Accessing the ModSecurity Directory

To begin, we need to navigate back to the ModSecurity directory we created in the previous steps:

cd /etc/nginx/modsec

Optionally, you can change the ownership of this directory to your current user, to avoid having to use the sudo command for subsequent steps:

sudo chown -R $USER:$USER /etc/nginx/modsec/

Step 2: Downloading the OWASP CRS Archive

Next, we’ll download the OWASP CRS archive using the wget command. At the time of writing, the latest stable release is 3.3.4, but you should check the OWASP Release tag page for the most recent version.


The nightly build version can be downloaded for those who prefer to stay on the latest developments. This version contains the latest changes and improvements but may be less stable and requires frequent updates. It should be noted that this version should only be used for advanced users:


Step 3: Extracting the CRS Archive

Once the download is complete, extract the archive using the tar command:

tar -xvf v3.3.4.tar.gz

Remember to replace v3.3.4.tar.gz with the name of the file you downloaded if you’re using a different version.

Step 4: Configuring the CRS

Like the ModSecurity configuration we dealt with earlier, the OWASP CRS comes with a sample configuration file that we need to rename. Using the cp command, we’ll create a copy of this file, preserving the original as a backup:

sudo cp /etc/nginx/modsec/coreruleset-3.3.4/crs-setup.conf.example /etc/nginx/modsec/coreruleset-3.3.4/crs-setup.conf

Step 5: Enabling the OWASP CRS Rules

To enable the OWASP CRS rules, we need to modify the modsec-config.conf file:

sudo nano /etc/nginx/modsec/modsec-config.conf

In this file, add the following two lines to include the CRS configuration and rules:

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

Save the file (CTRL+O) and exit (CTRL+T).

Remember to replace coreruleset-3.3.4 with the version you downloaded if it’s different.

Save the changes (Ctrl+O) and exit the text editor (Ctrl+X).

sudo nginx -t

Step 6: Validating the Configuration and Restarting Nginx

Now that we’ve made these significant changes to our Nginx configuration, it’s crucial that we validate everything to ensure there are no syntax errors or misconfigurations. This validation step is like a “dress rehearsal” for the actual launch of the updated service.

To perform this “dry run”, use the following command:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

This message tells us that our configuration is correct and that our changes will not disrupt service.

However, if you encounter any error messages, carefully review them for clues about what might have gone wrong. The error messages are often quite informative and can guide you toward the problematic section of your configuration files.

With the successful dry run, we are now ready to make these changes live. For the new configuration to take effect, we need to restart the Nginx service:

sudo systemctl restart nginx

Section 7: Understanding and Utilizing the OWASP Core Rule Set

With the installation and setup of ModSecurity and the OWASP Core Rule Set (CRS) on your Nginx web server completed, it’s time to delve deeper into understanding and making use of the CRS. The CRS is rich with options and, while the default settings provide immediate, broad-spectrum protection for most servers, a thorough exploration of its capabilities will help you fine-tune its functionality to suit your specific needs.

Navigating the CRS Configuration

For a start, let’s familiarize ourselves with the CRS configuration file. This is where you’ll be able to tweak the various settings provided by the CRS. To open the configuration file, use the following command:

sudo nano /etc/nginx/modsec/coreruleset-3.3.4/crs-setup.conf

Inside this file, you’ll find a myriad of options, each with detailed explanations in the form of comments. It’s a treasure trove of information, and spending time understanding what each setting does will provide you invaluable insights into how the CRS operates and how it can be tailored to your requirements.

Scoring Modes in OWASP CRS

The CRS operates using a scoring system. There are two primary modes of operation:

Anomaly Scoring Mode

# -- [[ Anomaly Scoring Mode (default) ]] --

This is the default and recommended mode. In this mode, each rule that matches a request or response increases an ‘anomaly score’. After all inbound and outbound rules have been evaluated, the total anomaly score is checked against a threshold. If the score exceeds the threshold, a disruptive action is taken (defaulting to returning an HTTP 403 error). This mode offers great flexibility in setting blocking policies and produces highly informative audit logs.

Self-Contained Mode

# -- [[ Self-Contained Mode ]] --

In this mode, a matching rule results in an immediate action, stopping further evaluation. While this mode may lower resource usage, it offers less flexibility in blocking policy and less informative audit logs. Only the first detected threat is logged, similar to many Intrusion Detection Systems (IDS).

In general, Anomaly Scoring Mode is recommended for most users due to its flexibility and informative nature.

Paranoia Levels: Balancing Security and Usability

A unique feature of the CRS is its paranoia levels. These levels allow you to control how many rule checks contribute to your anomaly scores, essentially letting you tune the sensitivity of the CRS.

There are four paranoia levels:

  • Paranoia Level 1: This is the default level, recommended for most users. It enables most core rules and should rarely trigger false positives (FPs), or legitimate requests being mistakenly identified as malicious.
  • Paranoia Level 2: Designed for advanced users, this level enables additional rules, such as regex-based SQL and XSS injection protections. Some false positives may occur at this level.
  • Paranoia Level 3: For expert users, this level enables even more rules and limits on special characters used. Expect to handle more false positives at this level.
  • Paranoia Level 4: Only recommended for exceptional circumstances. This level restricts special characters further and is likely to produce a large number of false positives.

These paranoia levels allow you to find the right balance between security and usability. High paranoia levels can provide more comprehensive security, but they may also interfere with legitimate traffic, causing false alarms. Therefore, if you decide to increase the paranoia level, be prepared to add some exclusion rules for certain requests and applications to reduce false positives.

Customizing OWASP CRS Rules (Additional Learning)

An advantage of the OWASP CRS is its flexibility that allows you to create and modify rules to meet your specific needs. Let’s take a closer look at how you might approach this task.

Opening the Rule Files

The rule files are located in the rules directory under the coreruleset-3.3.4 directory. These files contain sets of rules that the CRS uses to inspect incoming and outgoing HTTP traffic. You can open these files to view the existing rules and add your custom rules.

sudo nano /etc/nginx/modsec/coreruleset-3.3.4/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf

This opens the file that contains the exclusion rules that are processed before the CRS rules.

Understanding Rule Structure

Each rule in ModSecurity has a unique structure. A typical rule might look like this:

SecRule ARGS "@rx attack" "id:1234,phase:2,t:none,log,deny,msg:'Attempted attack'"

Here’s a brief breakdown of the rule components:

  • SecRule: The directive that begins the rule.
  • ARGS: The variable to be inspected. ARGS refers to all arguments including GET, POST, and cookies.
  • "@rx attack": The operator and its argument. @rx is a regular expression match, and attack is the pattern to match.
  • "id:1234,phase:2,t:none,log,deny,msg:'Attempted attack'": The rule’s action list. This includes a unique ID for the rule, the phase in which the rule operates, transformations to apply (t:none means no transformations), the action to take if the rule matches (in this case, log the event and deny the request), and a message to log if the rule matches.

Creating Custom Rules

Creating a custom rule involves deciding what variable to inspect, what pattern to look for, and what action to take when a match is found. For example, you might want to block requests that contain a specific suspicious user-agent string. You’d create a rule that inspects the USER_AGENT variable for the suspicious string, and denies the request if it’s found.

SecRule REQUEST_HEADERS:User-Agent "@rx suspicious-user-agent" "id:1235,phase:1,t:none,log,deny,msg:'Suspicious User-Agent Detected'"

This rule will block any request that includes the ‘suspicious-user-agent’ string in its user-agent header.

Remember, the process of creating custom rules requires a deep understanding of HTTP, the OWASP CRS, and your own application’s behavior. Be prepared to test thoroughly and adjust as necessary to avoid disrupting legitimate traffic.

Section 8: Verifying OWASP CRS Implementation

After successfully setting up the OWASP CRS on your server, it’s vital to test its implementation to ensure it’s functioning as expected. This step is crucial as it verifies the effective deployment of the security rules and allows for troubleshooting any potential issues that might have occurred during the setup.

Performing a Test Request

To confirm the efficacy of the OWASP CRS, you can use a specifically crafted URL that should trigger an alert if the rules are correctly implemented. This URL includes a query string that attempts to execute a command, a clear violation of security rules.

Replace with your actual domain name. This URL attempts to pass the /bin/bash command through the exec parameter in the query string.

Interpreting the Response

If OWASP CRS is correctly set up and operational, it should identify this malicious request and respond with a 403 Forbidden error. This error implies that the server understood the request but refuses to authorize it, which in this case is a result of the ModSecurity rule violation.

Example of 403 block and demonstrating ModSecurity 3 and OWASP CRS works correctly with Nginx:

The image above illustrates what you should see – a 403 Forbidden error page. If you receive this error, it signifies that ModSecurity and OWASP CRS are correctly configured and actively protecting your server against malicious requests.


If you don’t receive a 403 Forbidden error, it indicates that something is amiss. The most common issue is forgetting to switch ModSecurity from DetectionOnly mode to On. DetectionOnly mode logs the threats but doesn’t block them. Ensure you’ve made this change as outlined earlier in the tutorial.

Section 9: Managing False Positives and Custom Rule Exclusions

In the realm of cybersecurity, the deployment of the OWASP CRS alongside ModSecurity often serves as an effective line of defense. However, it can lead to instances of false positives, which can consume a significant amount of your time to manage. This time expenditure, though potentially overwhelming, is often justified by the valuable protection provided.

Navigating False Positives

The balance between security and usability is a delicate one. If the paranoia level of the ModSecurity rules is set too high, it can lead to an overwhelming number of false positives. Hence, it’s prudent to start at a lower paranoia level, allowing the rule set to run for several weeks to months with minimal false positives. This initial phase will help you to familiarize yourself with the behavior and alerts of the system. Once you feel comfortable with the output, you can gradually increase the paranoia level, ensuring you are not flooded with false positives at once.

Excluding Common False Positives

ModSecurity provides a mechanism to whitelist common actions that often lead to false positives. This functionality is particularly useful for applications that frequently interact with your server. An example of a default rule that whitelists such actions is given below:

#SecAction \
# "id:900130,\
#  phase:1,\
#  nolog,\
#  pass,\
#  t:none,\
#  setvar:tx.crs_exclusions_cpanel=1,\
#  setvar:tx.crs_exclusions_dokuwiki=1,\
#  setvar:tx.crs_exclusions_drupal=1,\
#  setvar:tx.crs_exclusions_nextcloud=1,\
#  setvar:tx.crs_exclusions_phpbb=1,\
#  setvar:tx.crs_exclusions_phpmyadmin=1,\
#  setvar:tx.crs_exclusions_wordpress=1,\
#  setvar:tx.crs_exclusions_xenforo=1"

To whitelist certain applications, uncomment the corresponding lines and leave the (1) number intact. For services that you do not use, for instance, Xenforo, change the (1) to (0). This action ensures that you do not whitelist unnecessary rules.

Streamlining the Syntax

For a cleaner syntax, you can also modify the command to include only the applications you wish to whitelist. For example, if you use WordPress, phpBB, and phpMyAdmin, the command would look like this:

SecAction \

This streamlined syntax makes it easier to read and maintain.

Addressing Custom Exclusions

To handle custom exclusions, you need to activate a specific file. This step involves renaming the REQUEST-900-EXCLUSION-RULES-BEFORE-CRS-SAMPLE.conf file using the cp command:

sudo cp /etc/nginx/modsec/coreruleset-3.4-dev/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example /etc/nginx/modsec/coreruleset-3.4-dev/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf

Each exclusion rule must carry a unique id. If a rule id is repeated, it would cause an error during the Nginx service testing phase.

Whitelisting Specific Paths and IP Addresses

There are instances where specific paths, often associated with certain applications, tend to trigger false positives. In such situations, you can whitelist these paths to eliminate unnecessary alerts. Here’s how you can do it:

SecRule REQUEST_URI "@beginsWith /wp-load.php?wpmudev" "id:1544,phase:1,log,allow,ctl:ruleEngine=off

SecRule REQUEST_URI "@beginsWith /ngx_pagespeed_beacon" "id:1554,phase:1,log,allow,ctl:ruleEngine=off"

In the above example, any URL that begins with the specified path will be automatically allowed, reducing the occurrence of false positives.

Sometimes, you may want to whitelist specific IP addresses that are known to be safe. ModSecurity provides options for this as well:

SecRule REMOTE_ADDR "^195\.151\.128\.96" "id:1004,phase:1,nolog,allow,ctl:ruleEngine=off"

## or ###

SecRule REMOTE_ADDR "@ipMatch,," "phase:1,id:1313413,allow,ctl:ruleEngine=off"

In these examples, the @ipMatch directive can be used to whitelist entire subnets, increasing the versatility of your whitelisting strategy. If you need to blacklist a specific IP address or subnet, simply replace allow with deny.

Refining Rule Exclusions

While whitelisting entire paths or IP addresses can be effective in reducing false positives, it might also unintentionally allow potentially harmful requests. A more precise approach is to disable specific rules that trigger false positives, rather than whitelisting entire paths. However, this approach requires more time and testing to fine-tune.

For instance, let’s say the rules with IDs 941000 and 942999 keep triggering false alerts for your /admin/ area. You can disable these specific rules by using the ruleRemoveById directive as shown below:

SecRule REQUEST_FILENAME "@beginsWith /admin" "id:1004,phase:1,pass,nolog,ctl:ruleRemoveById=941000-942999"

With this configuration, only the specified rules will be disabled for the /admin/ path, maintaining a high level of protection for the rest of your application.

Section 10: Implementing Log Rotation for ModSecurity

When working with a web application firewall like ModSecurity, it’s crucial to keep an eye on the logs. However, these logs can quickly grow in size, potentially taking up significant storage space on your server. This growth can also make it more difficult to find specific entries when you need to troubleshoot or analyze security events. To manage this, we can set up a process known as log rotation.

Creating the ModSecurity Log Rotation File

To begin with, let’s create a configuration file for the log rotation process. This file will define how and when the log rotation happens. We can place this file in the /etc/logrotate.d/ directory, which is where the logrotate utility looks for its configuration files.

Use the following command to create and open a new file called modsec:

sudo nano /etc/logrotate.d/modsec

The nano command launches a text editor in the terminal, allowing you to directly create and edit the file.

Defining the Log Rotation Rules

Inside this file, we’re going to define the rules for rotating the ModSecurity logs. Copy and paste the following block of code into the file:

        rotate 31

Let’s break down what each line in this block of code does:

  • /var/log/modsec_audit.log: This is the file that we want to rotate. It’s the location of the ModSecurity audit log.
  • rotate 31: This directive means that logrotate should keep 31 rotated log files before discarding the oldest ones.
  • daily: This instructs logrotate to rotate the log file once every day.
  • missingok: If the log file is missing, logrotate will not output an error message.
  • compress: This directive tells logrotate to compress the rotated log files to save space.
  • delaycompress: This postpones compression of the previous log file to the next rotation cycle. This can be helpful if some programs are still writing to the file.
  • notifempty: If the log file is empty, logrotate will not rotate it.

With these settings, your ModSecurity logs will be rotated daily, and you’ll keep a month’s worth of logs. However, you can adjust these settings to better fit your needs. For example, if you prefer to keep just a week’s worth of logs, you can change rotate 31 to rotate 7. But be mindful that for ModSecurity, daily rotation is recommended due to the potentially large size of the logs, and a weekly log file could be quite unwieldy to manage and analyze.

Section 11: Preventing Nginx Updates with APT-HOLD

As we navigate through the complex world of managing web servers, it’s important to recognize that automated system updates, while generally beneficial, can sometimes interfere with our carefully configured setups. This is especially true when we have customized or highly specific configurations, like in the case of Nginx with ModSecurity. In such cases, automated updates to Nginx can potentially overwrite our configurations or introduce compatibility issues with the current setup. Hence, it might be beneficial to prevent automatic updates to Nginx. We can achieve this by using a feature of the APT package management system known as “APT-HOLD”.

Understanding APT-HOLD

The apt-mark command is a utility that allows us to manipulate the package status within the APT package management system. One of its functionalities is the “hold” option, which can be used to mark a package as held back, which means it will not be updated.

Applying APT-HOLD to Nginx

To put Nginx on hold and thus prevent it from being automatically updated, we can use the apt-mark command as follows:

sudo apt-mark hold nginx

After running this command, the system will hold back any updates for the Nginx package until you manually change this setting. This will ensure that your configuration remains intact and unaffected by any automatic system updates.

Reverting APT-HOLD

At some point, you may want to update Nginx, perhaps to take advantage of new features or to apply security patches. To allow updates to Nginx once again, you can remove the hold by using the unhold option:

This will return the package to its regular status and will be updated the next time you update your system’s packages and are ready to deal with any issues between Nginx and Modsecurity, such as recompiling.

Closing Thoughts on Installing ModSecurity 3 with Nginx on Ubuntu Linux

In the journey we’ve embarked on together, we’ve successfully installed ModSecurity 3, integrated it with Nginx, and adopted the OWASP CRS on an Ubuntu Linux system. We’ve ensured that our web server now boasts a robust, open-source firewall, providing a solid line of defense against various web-based attacks. This endeavor, while complex, adds a significant layer of security to our web applications, safeguarding our operations and data.

From setting up the prerequisites and installing ModSecurity, to configuring Nginx and implementing OWASP CRS, each step was a critical piece of the overall security puzzle. We then learned to handle the logs generated by ModSecurity and manage their sizes for optimized performance. Lastly, we found a way to safeguard our specific Nginx configuration by disabling automatic updates. This entire process, while detailed and thorough, was a necessary undertaking to ensure that we have the most secure setup possible.

Additional Resources

In the ever-evolving world of cybersecurity, continuous learning is paramount. The following official resources will provide more in-depth information and assist you in your quest for knowledge:

  • ModSecurity Official Documentation: This is the official ModSecurity documentation, where you can dive deeper into the functionalities, capabilities, and configurations of ModSecurity.
  • OWASP CRS Official Github Page: The official Github page of OWASP CRS, where you can find the latest updates, FAQs, and detailed guidelines for using OWASP CRS.
  • Nginx Official Documentation: This is the official documentation of Nginx, a comprehensive guide to installing, configuring, and managing Nginx.

Share to...