Setup An Nginx Reverse Proxy – Ubuntu 20.04

Background

Since moving to an area served by gigabit fiber, I decided I didn’t want to pay for web hosting anymore, now that I had excellent connectivity. In addition to this website you are reading right now, I also host other web properties as well. I only have a single static public IP address but have more than one web application/site/service on my internal network, so I needed a way to proxy inbound requests from the internet to the appropriate inside destination. For this, I thought Nginx would be perfect. Here’s how I set it up.

Solution

First, I’ll summarize the high level steps in this project:

  • Install a new VM with Ubuntu Server 20.04
  • Install Nginx packages
  • Configure Nginx

I won’t get into the Ubuntu Linux install – I assume you already know how to do that. In my case, I installed to a VM (virtual machine), but you can do the same thing on a physical bare metal host if you like. Once Ubuntu was installed, it was time to install the necessary Nginx package:

sudo apt install nginx-extras -y

Once installed, it is time to configure Nginx to forward incoming requests based on the web site/application requested. I won’t get into how to setup SSL certificates, but you will see how I declare and place certificates in my configuration. All config files for Nginx are in /etc/nginx/

First thing I did was create a “certs” directory to store my SSL certs. I’ll need these later when configuring each site. In /etc/nginx you’ll see the following directory/files:

drwxr-xr-x  10 root root 4096 Dec 27 12:27 ./
drwxr-xr-x 100 root root 4096 Dec 14 06:51 ../
drwxr-xr-x   2 root root 4096 Dec  4 19:10 certs/
drwxr-xr-x   2 root root 4096 Nov 10 06:38 conf.d/
-rw-r--r--   1 root root 1077 Feb  4  2019 fastcgi.conf
-rw-r--r--   1 root root 1007 Feb  4  2019 fastcgi_params
drwxr-xr-x   8 root root 4096 Dec 27 12:32 .git/
-rw-r--r--   1 root root 2837 Feb  4  2019 koi-utf
-rw-r--r--   1 root root 2223 Feb  4  2019 koi-win
-rw-r--r--   1 root root 3957 Feb  4  2019 mime.types
drwxr-xr-x   2 root root 4096 Nov 10 06:38 modules-available/
drwxr-xr-x   2 root root 4096 Nov 18 17:34 modules-enabled/
-rw-r--r--   1 root root 1490 Feb  4  2019 nginx.conf
-rw-r--r--   1 root root  180 Feb  4  2019 proxy_params
-rw-r--r--   1 root root  636 Feb  4  2019 scgi_params
drwxr-xr-x   2 root root 4096 Dec 21 03:47 sites-available/
drwxr-xr-x   2 root root 4096 Dec 20 21:22 sites-enabled/
drwxr-xr-x   2 root root 4096 Nov 18 17:34 snippets/
-rw-r--r--   1 root root  664 Feb  4  2019 uwsgi_params
-rw-r--r--   1 root root 3071 Feb  4  2019 win-utf

In my case, I got rid of the default config in “sites-available” and it’s symlink in “sites-enabled”. I created a new file called “websites-ssl.conf” in sites-available and symlinked it to sites-enabled. In this example, I show 2 sites configured and referred to an internal webserver called web01 and it’s ip (shown where x.x.x.x is the LAN IP address of such webserver). You can see that for each website we’re listening on ports 80 and 443. Requests to port 80 are immediately redirected to port 443 because we want people to use the SSL (secure socket layer) version of the site. You can see how each site has it’s SSL certs declared and how each site is logging to its own log file.

My websites-ssl.conf looks like:

upstream web01 {
        server x.x.x.x:443;
}
upstream web02 {
        server y.y.y.y:443;
}

server {
listen 80;
server_name wizworks.net www.wizworks.net; 
rewrite ^ https://$host$request_uri permanent;
}

server {
    listen 443 ssl;
    server_name wizworks.net;
    ssl_certificate /etc/nginx/certs/wizworks.net.crt;
    ssl_certificate_key /etc/nginx/certs/wizworks.net.key;
    ssl_session_cache builtin:1000 shared:SSL:10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    ssl_prefer_server_ciphers on;

    access_log /var/log/nginx/wiz-access.log;
    error_log /var/log/nginx/wiz-error.log;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass https://web01;
    }
}

server {
listen 80;
server_name otherwebsite.net www.otherwebsite.net; 
rewrite ^ https://$host$request_uri permanent;
}

server {
    listen 443 ssl;
    server_name otherwebsite.net;
    ssl_certificate /etc/nginx/certs/otherwebsite.net.crt;
    ssl_certificate_key /etc/nginx/certs/otherwebsite.net.key;
    ssl_session_cache builtin:1000 shared:SSL:10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    ssl_prefer_server_ciphers on;

    access_log /var/log/nginx/ow-access.log;
    error_log /var/log/nginx/ow-error.log;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass https://web02;
    }
}

Once the file is created, I then enabled the configuration by placing a symlink in /etc/nginx/sites-enabled:

cd /etc/nginx/sites-enabled
ln -s ../sites-available/websites-ssl.conf .

You can test the Nginx configuration by running “nginx -t”. As long as there’s no errors reported, you can then apply the new configuration by running:

sudo systemctl restart nginx

You should then check to make sure nginx is running:

sudo systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2023-01-05 18:00:09 UTC; 3h 23min ago
       Docs: man:nginx(8)
    Process: 96888 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 96900 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 96901 (nginx)
      Tasks: 5 (limit: 4611)
     Memory: 14.3M
     CGroup: /system.slice/nginx.service
             ├─96901 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             ├─96902 nginx: worker process
             ├─96903 nginx: worker process
             ├─96904 nginx: worker process
             └─96905 nginx: worker process

Jan 05 18:00:09 proxygw systemd[1]: Starting A high performance web server and a reverse proxy server...
Jan 05 18:00:09 proxygw systemd[1]: Started A high performance web server and a reverse proxy server.

If you see output like the above, then your Nginx configuration is applied and running and web traffic should be flowing to your internal webserver.

Leave a Reply

Your email address will not be published. Required fields are marked *