Install and Harden NGINX with ModSecurity on Ubuntu

By Raman Kumar

Updated on Sep 30, 2025

In this tutorial, we'll learn how to install and harden NGINX with ModSecurity on Ubuntu 24.04 server.

When we set up NGINX as a web server or reverse proxy, security cannot be left as an afterthought. A hardened NGINX with ModSecurity Web Application Firewall (WAF) provides protection against common threats such as SQL injection, XSS, and brute force attempts. Below is a complete step-by-step guide on how to install and harden NGINX with ModSecurity on Ubuntu 24.04.

Prerequisites

Before we begin, ensure we have the following:

Install and Harden NGINX with ModSecurity on Ubuntu

Step 1: Update System Packages

Before making any changes, we must ensure that our server is running the latest packages.

sudo apt update && sudo apt upgrade -y

Keeping the system up to date closes vulnerabilities that attackers often exploit.

Step 2: Install NGINX

Ubuntu 24.04 provides the latest stable NGINX through its official repositories. We install it with:

sudo apt install nginx -y

Enable and start the service:

sudo systemctl enable nginx
sudo systemctl start nginx

Check if it’s running:

systemctl status nginx

Step 3: Install ModSecurity

We need ModSecurity as a dynamic module for NGINX. First, install required dependencies:

sudo apt install libnginx-mod-http-modsecurity -y

This package includes ModSecurity built for NGINX.

Step 4: Enable ModSecurity in NGINX

The module is installed but not enabled by default. We need to load it.

Edit the main configuration file:

sudo nano /etc/nginx/nginx.conf

Inside the http block, add:

modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;

Step 5: Configure ModSecurity Core Settings

Now we configure ModSecurity to run in Detection Only Mode first. This allows us to monitor requests without blocking legitimate traffic.

Create the ModSecurity directory:

sudo mkdir /etc/nginx/modsec

Copy the default configuration:

sudo cp /etc/modsecurity/crs/crs-setup.conf /etc/nginx/modsec/
sudo cp -r /usr/share/modsecurity-crs/rules /etc/nginx/modsec/

Then copy the main configuration:

sudo curl -o /etc/nginx/modsec/modsecurity.conf \
  https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended

Switch to blocking mode:

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

Find the line:

SecRuleEngine DetectionOnly

Change it to:

SecRuleEngine On

Save and exit.

Step 6: Include OWASP Core Rule Set (CRS)

The OWASP CRS provides a strong baseline against common web attacks. We include it in the ModSecurity main configuration file:

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

Add:

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

Grab the official unicode.mapping file

Pull it straight from OWASP’s repo:

sudo curl -o /etc/nginx/modsec/unicode.mapping \
  https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/unicode.mapping

Open your /etc/nginx/modsec/modsecurity.conf:

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

Find this line (around 258):

SecUnicodeMapFile unicode.mapping

Replace to:

SecUnicodeMapFile /etc/nginx/modsec/unicode.mapping 20127

Step 7: Test NGINX and Reload

Check syntax:

sudo nginx -t

If there are no errors, reload NGINX:

sudo systemctl reload nginx

Step 8: Verify ModSecurity Logs

ModSecurity logs blocked requests to /var/log/nginx/error.log or a dedicated log file if configured.

Example test request (simulates SQL injection):

curl "http://localhost/?id=1' OR '1'='1"

Output may look like:

curl: (3) URL rejected: Malformed input to a URL function

Check the error log:

sudo tail -f /var/log/nginx/error.log

If rules are working, the request will be logged as blocked.

Step 9: Harden NGINX Configuration

Besides ModSecurity, we must strengthen NGINX itself:

1. Disable Server Tokens

sudo nano /etc/nginx/nginx.conf

Inside http block, add:

server_tokens off;

2. Enable Only Strong TLS Protocols

sudo nano /etc/nginx/snippets/ssl-params.conf

Add:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!MD5;

Include this snippet in site configurations.

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    include snippets/ssl-params.conf;

    root /var/www/html;
    index index.html;
}

3. Limit Request Methods

Inside server block:

if ($request_method !~ ^(GET|POST|HEAD)$) {
    return 444;
}

This must also go inside a server {} block — not in http {} and not in location {}. Put it just after your server_name and root directives so it applies to all locations for that domain.

4. Restrict Buffer and Body Sizes

client_body_buffer_size 10K;
client_header_buffer_size 1k;
client_max_body_size 1M;
large_client_header_buffers 2 1k;

Example placement:

http {
    server_tokens off;

    client_body_buffer_size 10K;
    client_header_buffer_size 1k;
    client_max_body_size 1M;
    large_client_header_buffers 2 1k;

    include /etc/nginx/mime.types;
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Step 10: Restart and Monitor

Restart NGINX:

sudo systemctl restart nginx

Monitor for any issues:

sudo journalctl -u nginx -f

Final Thoughts

By installing NGINX with ModSecurity WAF on Ubuntu 24.04 and applying additional hardening steps, we establish a solid defense against common web attacks. With the OWASP Core Rule Set, we gain community-tested protection, while NGINX tweaks ensure tighter control over connections and traffic handling. Regular updates, monitoring, and fine-tuning rules will keep our environment secure and reliable