Install and Harden NGINX with ModSecurity on Ubuntu

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:
- An Ubuntu 24.04 on dedicated server or KVM VPS.
- Basic Linux Command Line Knowledge.
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
