An Application Firewall for Nginx

application firewallAs of sometime in 2022, Nginx (pronounced “engine x”) is the most widely used web server, ahead of Apache and Cloudflare Server. An application firewall designed for Nginx can enhance its capabilities and make administering the web server even easier. While there are probably infinite ways to set things up, I’m going to tell you how I have things set up.

The Application Firewall Files

This application firewall requires two files, and only because the GEO and MAP directives have to run at the HTTP level. I call the file at the HTTP level “drop.conf” and it’s included in the “/etc/nginx/nginx.conf” file, with “include /etc/nginx/conf.d/drop.conf”.

I call the second file at the SERVER level “actions.conf” and it’s included in the “etc/nginx/sites-available/default” file, with “include /etc/nginx/conf.d/actions.conf”. If you’re running multiple websites, you can use the appropriate “sites-available” file instead of “default”, of course.

GEO and MAP Directives

I use them like this:

geo $dropip {
default 0; 1; 1;

The first is a simple CIDR and the second is a specific IP address.

map $request_uri $dropuri {
default 0;
~*^/login\.php 1;
~*/info\.php 1;

I collect failed URI strings from the access log. The error code is usually “404”.

map $http_referer $dropref {
~*173\.230\.149\.128 1;

It only makes sense to drop obviously incorrect referrers. Since my server’s IP address would never show up in my logs for someone moving from page to page, it makes perfect sense to block it. Since real referrer URL strings would include the schemes (“http” or “https”), it also makes perfect sense to block those that don’t include them.

map $http_user_agent $dropua {
default 0;
~*Hello\ World 1;
~*Mozilla/3\.0 1;

I only block user agents that are obviously fake or are one of the many scraper bots. I don’t have a list of bad user agents to give out and I only block them when I see them in the logs.

The Actions

The “actions.conf” file includes lines like these:

if ($dropip)    { return 444; }
if ($dropref)   { return 444; }
if ($dropua)    { return 444; }
if ($dropuri)   { return 444; }

Returning the 444 error code simply means returning nothing at all, and only Nginx understands that. It doesn’t matter if you agree with me, but thousands of 404 errors in a row will consume all kinds of resources.

I have other “includes”, but they don’t belong in the context of an application firewall. All the rewrite directives, for example, are in another included file.

It doesn’t take long for me to peruse the access and error logs every day. I always find 404 errors in the access log and failed SSL handshakes in the error log. I’m usually examining the log files when I’m not blogging about something. Although I always find these errors, the numbers I find every day continue to decrease. I’ll be happy when I don’t see any at all in a single given day.

Image by Luis F. Gonzalez, Public domain, via Wikimedia Commons

Leave a Comment