Setting up nginx as a reverse proxy server

To make multiple http-services available on a single IP you need to use a reverse proxy, it will see the requested domain name in the http request and forward it to the correct server. It can also do simple port translation and provide https, all managed from a single server.

There are ready made solutions available like HAProxy and Varnish, but in my simple setup I prefer doing it myself with nginx, fail2ban, Certbot and GeopIP2.

I have decided to use a VM with Debian 12. After installing the OS update your packages and install nginx-full, certbot, fail2ban. Before setting up the reverse proxy I spend some time securing the system. It is important to block at least invalid http-request and bad ssh login attempts. Also make sure your system is behind a firewall and allow only TCP port 80 and 443 traffic. SSH to this server is only possible from the local network.

to block host after too many bad request I use fail2ban. after installation edit the config file /etc/fail2ban/jail.local and make sure sshd and nginx is configured:

Another security measure I like to take is blocking access to some web-services from outside my country. Some are services are only made publicly available for my personal use or to share with family and friends and they always live in Belgium, if not I will provide them VPN access.

An easy solution is using GeoIP2. I have configured it on nginx, but if you prefer you can also do something similar on some firewalls. Most of the time this is a paid service, but Maxmind allows you to use it to block by country for free.

Go to the Maxmind website and create an account. After logging in go to Manage License Keys and create a new key for your server. Now just click on the Download config button and save your GeoIP.conf-file in /etc on your server.

The next thing to do is downloading the GeoIP-databases you can use. I have downloaded GeoLite2-City and GeoLite2-Country from the Download Files page to my pc, unpacked the files and uploaded them to /usr/share/GeoIP/ on my server. You can also get a permalink to download them directly to your server, this way you can use a simple bash-script to schedule future updates using cron.

The last thing to do is adding the GeoIP config in /etc/nginx/nginx.conf or a separate config file. I have just added the following config at the end of nginx.conf:

##
# GeoIP block config
##

geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
auto_reload 5m;
$geoip2_metadata_country_build metadata build_epoch;
# $geoip2_data_country_code default=US source=$variable_with_ip country iso_code;
$geoip2_data_country_code default=US country iso_code;
$geoip2_data_country_name country names en;
}

map $geoip2_data_country_code $allowed_country {
default no;
BE yes;
}

The important config is at the end, the default action is to allow no countries with BE as the exception. This make it easy and it is easy to quickly add other countries when necessary.

Now I am finally ready to add the configuration for the websites to nginx. I will host some domains directly on this server and forward other domains to the correct server. To easily add new domains I will make two config templates in /etc/nginx/sites-available, one for hosting and one for reverse proxy.

The hosting template

In this template I just have to set the correct name and path and decide if region blocking is preferred. By commenting out the if ($allowed_country = no) block I can disable the country blocking. I also need to add the correct folder and files for that website and enable the site.

The proxy template

The reverse proxy config is very similar, but will point to another server as the destination. I only have to configure the correct IP and port and make sure the firewall allows access from the reverse proxy.

To enable a site you have to link the config-file from sites-available to sites-enabled and reload the nginx config.

root@debian-proxy:~# ln -s /etc/nginx/sites-available/thierryaerts.be /etc/nginx/sites-enabled/thierryaerts.be

At this point the site will be online but only using http, so the last step is to make sure the site domain gets a valid certificate and the https config is added. This step is very easy and will all be handled by Certbot. just start certbot with admin privileges on your server and select the domain you wish to add a certificate for.

Certbot will not only request the free Let's Encrypt certificates, but also add the https config to your site and run a service to refresh the certificates when they expire. So I would always recommend to use nginx and certbot and avoid setting up a certificate on every single web service that you might have running, it is just so much more simple to maintain the certificates on a central location!