How to block direct IP access to your Nginx web server

Nginx by default does not block unwanted access to your web server when it's done directly to your servers IP addresses. Here's how you can fix this behaviour to protect your servers resources.

When you set up a new Nginx web server, what you will usually do is to configure a virtual host for the domain that you are about to use.

Although you would expect that web traffic will only hit your website using that specific domain, this is not true. Instead, you will be able to open URLs like http://<IP address>/, https://<IP address>/ without a problem. By using your servers IP addresses (IPv4 and IPv6). Usually crawlers and bots do exactly that, just because they know the specific IP range of your hosting provider. While it doesn’t seem like a high risk, it’s unneccessary to have Nginx handle these requests. And more importantly, this basically allows any nameserver to point to your server with a 3rd party domain via A and CNAME records.

And that’s a likely scenario, especially if someone has “owned” or “used” your specific IP address in the past.

To eliminate any risk, and prevent Nginx from handling requests without a domain name, we will make a simple change:

Step 1: Your default_server should close all connections

Modify your Nginx default.conf, which is intended to act as a fallback like this:

/etc/nginx/sites-available/default.conf
server { listen 80 default_server; listen [::]:80 default_server; server_name ""; return 444; #CONNECTION CLOSED WITHOUT RESPONSE }

What this does is to actually close the connection, whenever a request hits Nginx and a domain name is missing.

In the next step, we will tell your virtual host, to do the same.

Step 2: Your virtual host should check the server name and close unwanted connections as well

In each server block of your virtual host, add the following lines. With each server block, I am referring to blocks that listen on port 80 as well as blocks that listen on port 443.

/etc/nginx/sites-available/example.com
server { ... # Only allow access if the host is correct if ( $host != "example.com" ){ return 444; #CONNECTION CLOSED WITHOUT RESPONSE } ... }

If the server_name directive has multiple domain names (eg: www and non-www domain), you should test for all:

/etc/nginx/sites-available/example.com
server { ... # Only allow access if the host is correct set $test 0; if ( $host != "example.com" ){ set $test 1; } if ( $host != "www.example.com" ){ set $test 1$test; } if ( $test = 11 ){ return 444; #CONNECTION CLOSED WITHOUT RESPONSE } ... }

This basically closes the connection whenever the request was not made to the domain that you expected.

Step 3: Repeat step 2 for all other domains

This is necessary because servers with multiple vitual hosts usually configure each individual domain to listen on the exact same ports (usually 80 and/or 443). Now if a single virtual host, does respond to domain-less request, then all the other domains would show the virtual host, that didn’t block the direct IP traffic. And this you don’t want. So repeat step 2 for all domains.

Step 4: Reload nginx and test the results

/etc/init.d/nginx restart

Done.