Apache2 eats a lot of RAM to I tried nginx but only to find out that you need to compile the modsecurity and nginx itself.

Let us start:

Install prerequisite packages:

$ sudo apt update && sudo apt-get install -y apt-utils autoconf automake build-essential git libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre++-dev libtool libxml2-dev libyajl-dev pkgconf wget zlib1g-dev
$ sudo apt install nginx tmux
$ nginx -V

You need to copy the configure arguments in the nginx -V output, we will mimic that when we compile our own nginx, now we need to remove the nginx. ($ sudo apt remove nginx)

Download the modsecurity, compile then install:

$ tmux
$ git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity
$ cd ModSecurity
$ git submodule init
$ git submodule update
$ ./build.sh
$ ./configure
$ make
$ sudo make install

Download the nginx connector:

$ git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git

Download and verify the latest nginx code either mainline or stable: WE will use mainline. To verify the package you need the gpg package and the nginx authors public key.

$ wget -c https://nginx.org/download/nginx-1.13.11.tar.gz
$ wget -c https://nginx.org/download/nginx-1.13.11.tar.gz.asc
$ gpg --verify nginx-1.13.11.tar.gz.asc nginx-1.13.11.tar.gz

example output:

gpg: Signature made Tuesday, 11 July, 2017 09:26:17 PM +08
gpg:                using RSA key B0F4253373F8F6F510D42178520A9993A1C052F8
gpg: Good signature from "Maxim Dounin <mdounin @ mdounin.ru>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: B0F4 2533 73F8 F6F5 10D4  2178 520A 9993 A1C0 52F8

If it says GOOD SIGNATURE then we are ok otherwise redownload the package.

Unpack the package and modify the source code. We will hide the nginx server name.

Modify the file:

$ nano src/http/ngx_http_header_filter_module.c
Find the line and change nginx to your preferred name
static u_char ngx_http_server_string[] = "Server: nginx" CRLF;
static u_char ngx_http_server_full_string[] = "Server: nginx" CRLF;
static u_char ngx_http_server_build_string[] = "Server: nginx" CRLF;


We also need to modify the default error pages (HTTP) as it also indicates nginx name

$ nano src/http/ngx_http_special_response.c

find and edit the nginx name:

static u_char ngx_http_error_full_tail[] =
"<hr><center>nginx</center>" CRLF
"</body>" CRLF
"</html>" CRLF

static u_char ngx_http_error_build_tail[] =
"<hr><center>nginx</center>" CRLF
"</body>" CRLF
"</html>" CRLF

static u_char ngx_http_error_tail[] =
"<hr><center>nginx</center>" CRLF
"</body>" CRLF
"</html>" CRLF

You may want to edit also the http error codes
for the 301 HTTP RESPONSE

static char ngx_http_error_301_page[] =
"<html>" CRLF
"<head><title>HTTP MOVED</title></head>" CRLF
"<body bgcolor=\"white\">" CRLF
"<center><h1>I moved it</h1></center>" CRLF



We will now configure, compile and install nginx. Append your configure parameters, please read up at nginx website.

$ ./configure --add-module=../ModSecurity-nginx --with-compat --add-module=../headers-more-nginx-module
$ make
$ sudo make install

$ nginx -V
nginx version: nginx/1.13.11
built by gcc 6.3.0 20170516 (Raspbian 6.3.0-18+rpi1+deb9u1) 
built with OpenSSL 1.0.2l  25 May 2017
TLS SNI support enabled
configure arguments: --add-module=../../ModSecurity-nginx --with-compat --add-module=../../headers-more-nginx-module


Start nginx

$ sudo nginx

Check if nginx is running

$ ps -ef|grep nginx

If you want to start nginx on boot using systemd, you can use the masked nginx since we uninstalled it earlier:

$ sudo systemctl unmask nginx
$ sudo systemctl start nginx
$ sudo systemctl status nginx

If the nginx is not installed in the /usr/sbin you can copy the objs/nginx to /usr/sbin this will be depending on your configure arguments.

Problems remaining is you need to recompile nginx if a new version comes out and I cannot seen to change the nginx server name so it will be security through obscurity.

Now to test the modsecurity and install Core Rules Set

$ sudo mkdir /etc/nginx/modsec
$ sudo wget -P /etc/nginx/modsec/ https://raw.githubusercontent.com/SpiderLabs/ModSecurity/master/modsecurity.conf-recommended
$ sudo mv /etc/nginx/modsec/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf

Activate modsec

$ sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/nginx/modsec/modsecurity.conf

edit /etc/nginx/modsec/main.conf: and add the following

SecRule ARGS:testparam "@contains test" "id:1234,deny,status:403"


In your site config (eg. site-enabled/mysite.conf) add in the server directives

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

Try if it works:

$ curl localhost?testparam=test
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
$ curl-I localhost?testparam=test
HTTP/2 403 
date: Sun, 08 Apr 2018 05:01:52 GMT
content-type: text/html
content-length: 331
server: nginx


Adding the OWASP CRS
Download the latest crs from https://github.com/SpiderLabs/owasp-modsecurity-crs
or git clone the repo.

unpack the archive if you downloaded tar.gz

$ tar zxvf v3.0.2.tar.gz
$ sudo mv owasp-modsecurity-crs-3.0.2 /usr/local
$ cd /usr/local/owasp-modsecurity-crs-3.0.2
$ sudo mv crs-setup.conf.example crs-setup.conf

edit the /etc/nginx/modsec/main.conf and add

Include /usr/local/owasp-modsecurity-3.0.2/crs-setup.conf
Include /usr/local/owasp-modsecurity-3.0.2/rules/*.conf

Reload or Restart nginx

$ sudo nginx -s reload
$ sudo systemctl reload nginx

try to crack your site using nikto or other tools.

Configuring Fail2ban, in my distro there are nginx filter.d configurations available. We will just enable it.
Edit your default jail.d/

enabled = true
port = https

enabled  = true
port = https
filter   = nginx-noscript
logpath  = /var/log/nginx/*access.log
maxretry = 6

enabled  = true
port     = https
filter   = nginx-badbots
logpath  = /var/log/nginx/*access.log
maxretry = 2

enabled  = true
port     = https
filter   = nginx-nohome
logpath  = /var/log/nginx/*access.log
maxretry = 2

enabled  = true
port = https
filter   = nginx-noproxy
logpath  = /var/log/nginx/*access.log
maxretry = 2

#I added an additional filter because webserver are being attacked by malformed URL
enabled  = true
port     = https
filter   = nginx-x00
logpath  = /var/log/nginx/*access.log
bantime  = 86400
findtime = 86400
maxretry = 2

enabled  = true
port     = https
filter   = nginx-custom
logpath  = /var/log/nginx/*access.log
bantime  = 86400
findtime = 600
maxretry = 2

Adding the additional filters:

$ sudo nano /etc/fail2ban/filter.d/nginx-x00.conf   

failregex = ^ .* ".*\\x.*" .*$
ignoreregex =

$ sudo nano /etc/fail2ban/filter.d/nginx-custom.conf

failregex = ^ .* "(GET|POST|HEAD) [^"]+" 400
            ^ .* "(GET|POST|HEAD) [^"]+" 403
            ^ .* "(GET|POST|HEAD) [^"]+" 404
            ^ .* "(GET|POST|HEAD) [^"]+" 405
ignoreregex =

You can check my earlier post about implementing UFW with fail2ban.

You can view the logs of nginx in a nice format, install lnav or use goaccess.
In lnav:

 $ sudo lnav /var/log/nginx/access.log

Generate HTML in GOAccess or just use the console
In GoAccess:

 $ sudo cat /var/log/nginx/*access.log | sudo goaccess -o /var/www/html/GoAccess.html --log-format=COMBINED

Except where otherwise noted, this work is licensed under Creative Commons Attribution-ShareAlike 4.0 International License (http://creativecommons.org/licenses/by-sa/4.0/).
I hope that this post is useful to you, if you liked this post you may support me via liberapay. Thank you for your support.

Donate using Liberapay