Japanatron Logo

What follows is an outline I compiled while researching how to tighten security on a Nginx web server.
NOTE 1: Ubuntu 14.04 LTS was used for this.
NOTE 2: This is a continuation of this article: Joomla Running on Nginx and Ubuntu Server - Setting Up

Turn Off Server Tokens
Why disclose the Nginx version?  Open nginx.conf and uncomment or add this line:

server_tokens off;

Limit Allowed Methods
Block anything other than GET and POST by putting this in the virtual host's location block:

# Limit request methods
        limit_except GET POST {
                deny all;

Rate Limiting - Main
This is great for blocking bots and DoS attacks by allowing only a defined number of requests per second per IP.  Establish the rate zone by adding this to the http block of nginx.conf.  The example below allows 1 request per second per remote IP address.

# Rate Limiting - Main
        limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

Then apply this rate zone to your virtual host by adding this to your PHP location block.  The main location block may be used instead, but that may cause false positives.  My research shows that the PHP location is really what needs the rate-limiting protection.  This applies the zone you just defined in nginx.conf.

# Request Rate Limiting
        limit_req zone=one burst=5 nodelay;

Rate Limiting - POST Method
I have a problem with spambots constantly trying to POST spammy comments to my site.  Although the captcha protection combined with Akismet does a great job of blocking the spam, I'm annoyed that server resources are spent on accepting this superfluous traffic.  Again, first define the zone in nginx.conf.  The example below allows 2 POST requests per minute--quite strict.  Maybe 1 per minute is even better.

# Rate Limiting - POST method
        map $request_method $postlimit {
          default         "";
          POST            $binary_remote_addr;

        limit_req_zone $postlimit zone=post:10m rate=2r/m;

Then apply this rate zone to your virtual host's main location block.

# Request Rate Limiting - POST method
        limit_req zone=post burst=5 nodelay;

NAXSI is a powerful application firewall for Nginx in desperate need of a new moniker.  There's already a ready-to-go Nginx binary with NAXSI included:

apt-get install nginx-naxsi

Enable it in nginx.conf by uncommenting this line:

# Nginx-NAXSI config
    include /etc/nginx/naxsi_core.rules;

Next enable it in the virtual host config in the main location block:

include /etc/nginx/naxsi.rules;

Next define the "request denied" URL in your virtual host config:

# For Naxsi firewall
        location /RequestDenied {
         return 405;

Finally, configure it here: /etc/nginx/naxsi.rules

NAXSI basically blacklists just about everything, so the general idea is to...
* Run it in "Learning Mode," (enabled by default) and look at your Nginx error logs for NAXSI events.  Learning Mode won't block any traffic, just log it.
* Surf your website, testing as much functionality as possible.  You'll probably generate a bunch of NAXSI events.  I certainly did.
* Generate a whitelist manually or using NAXSI's tools.  Personally, I went with the older deprecated one called "nx_util," because the newer one requires far more package prerequisites and know-how.
I didn't exhaustively document how to setup NAXSI because it's already been covered elsewhere on the web.  Please see the further reading section below.

Oh my Gods, I love this package.  It's the Occam's Razor of security measures.  It scans your logs looking for particular triggers (e.g. "access denied").  When a trigger fires a number of times (5 "access denied" log entries in 10 minutes from 1 IP address), it takes action (ban the IP address for 1 hour using IP tables).  Although there's only 1 included trigger for Nginx, Fail2Ban's plain-vanilla approach makes customization straightforward.

First I define a custom ban action.  I do this because I use a reverse proxy, and an IP tables ban doesn't work because it only sees the IPs of the reverse proxy.  Nginx sees the real IP, so I define an Nginx ban action here: /etc/fail2ban/action.d/
This action adds and removes IPs from an Nginx blocklist.

actionstart = touch /etc/nginx/conf.d/blocklist.conf
actionstop = rm /etc/nginx/conf.d/blocklist.conf
actioncheck =
actionban = echo "deny <ip>;" >> /etc/nginx/conf.d/blocklist.conf ; /usr/sbin/nginx -s reload
actionunban = sed -i /<ip>/d /etc/nginx/conf.d/blocklist.conf ; /usr/sbin/nginx -s reload

Next, I define the trigger(s) here: /etc/fail2ban/filter.d/
EXAMPLE 1: This trigger fires when a bad-boy IP is getting rate-limited.

before = common.conf

failregex = limiting.requests.*client\:.<HOST>
ignoreregex =

EXAMPLE 2: This one looks for POSTs to the site's comment system that are getting rate limited.

before = common.conf

failregex = ^<HOST>.*POST.\/\?option\=com\_comment.*503
ignoreregex =

EXAMPLE 3: This one looks for NAXSI firewall events.

before = common.conf

failregex = NAXSI_FMT: ip=<HOST>
ignoreregex = learning=1

The hardest part for me was figuring out the regex.  I suck at regex because it's harder to read than classical Klingon.  You'll have no problem defining custom Fail2Ban triggers if you're already skilled at regex.  These sites were a godsend for unskilled me:
* http://regexone.com/
* http://www.regexr.com/

Last, I enable the trigger & ban action in /etc/fail2ban/jail.local
For example...

enabled = true
port = http,https
filter = nginx-comment
logpath = /var/log/nginx/*access.log
maxretry = 1
banaction = nginx-deny

Further Reading
Please refer to the sites below for more information on locking-down Nginx.  Heartfelt thanks goes to the authors of these as I found them indispensable during my research.

Nginx Security


NAXSI - Consulting

Related Articles

KVM - Enable Direct PCI Pass-T...

This example was done on Ubuntu server.* Enable VM direct I/O in the BIOS.* Open /etc/default/grub using an editor like nano or something.* Find GRUB_CMDLINE_LI...

FreeNAS - Bi-Directional Rsync...

Go to /root on 1st server. ssh [email protected] cd /root FreeNAS OS drive is mounted read-only, so mount it RW. mount -o rw / Generate an RSA key & leave the ...

The Hunt for the Ultimate Free...

I spent some time researching top-rated, but free antivirus security software for both Mac and Windows.  There are far more free antivirus software solutions ou...

Ubuntu 22.04 Nginx Build Outli...

I re-built my LEMP web-server fresh on Ubuntu 22.04 and learned some things along the way. This is my base build outline mostly created for my own notes. INS...