How to configure NGINX as reverse proxy with TLS

Do you have a service running that needs to be reached via a domain name? Do you want to secure it with TLS? Read on to find out how you can use NGINX for this.

How to configure NGINX as reverse proxy with TLS

Introduction

If you've a service running or if you've a device which you want to be reachable via a domain name, or even perhaps if you want to secure the connection with TLS, you can setup and configure any modern day reverse-proxy, something like Traefik, Apache or NGINX. All of these offer best in class routing and security features out of the box. In this blog post, I'll be focussing on setting up and configuring NGINX to redirect an incoming requrest to the service you're running and securing the request with TLS.

NGINX

Nginx is an open-source tool written in C programming language, which has been around for nearly two decades, and as its github readme states, is the world's most popular Web Server, high performance Load Balancer, Reverse Proxy, API Gateway and Content Cache. NGINX is many thing, but we're only interested in its capabilities as a reverse-proxy enabling secure HTTPS connections with TLS (Transport Layer Security) so that the communication between your users and your backend servers is encrypted, protecting sensitive data from potential attackers. So, lets focus on how you can set it up and configure properly.

NOTE
It is assumed here that you've already added an entry to your domain name registrar - like Godaddy, Cloudflare etc with the IP address of the machine you are setting the service on.

Step 1: Install NGINX

Installation of NGINX is fairly simple and straight forward. The process takes advantage of package managers native to specific Linux distributions.

Debian / Ubuntu

For installing on debian or debian based linux like Ubuntu, you can use the following command

sudo apt update
sudo apt install nginx

CentOS / RHEL

On a Red Hat Enterprise linux or a CentOS linux, you can use the following command

sudo yum install nginx

Once installed, you can start the NGINX service:

sudo systemctl start nginx
sudo systemctl enable nginx

You can always check the running status of the service using the following command

sudo systemctl status nginx

You should get the following output

Output when nginx is running
Output when nginx is running

If you get a similar output just like shown above, then your nginx service has been installed correctly. Now lets see how you can configure it.

Step 2: Install Certbot

For secure TLS encryption, you'll need an SSL certificate. You can aquire a certificate from a trusted Certificate Authority (CA). There are many Certificate authorities like DigiCert, GlobalSign etc that sell credible SSL certificates for some price. Enterprise or big Corporates acquire certificates from these certification authorities. Another way is to generate a free certificate from Let's Encrypt. Here, we will use Let's Encrypt.

Now, to setup an SSL certificate, we first need a tool called certbot. It is the official tool provided by Lets encypt to obtain SSL certificates easily.

The following command installs the tool certbot along with the plugin python3-certbot-nginx which automatically updates the nginx configuration files whenever a new certificate is installed or an old certificate is renewed.

On Ubuntu/Debian:

On the debian linux or a debian based linux, use the following command to install the certbot

sudo apt install certbot python3-certbot-nginx

On CentOS/RHEL:

On a Red Hat Enterprise linux or a CentOS linux, you can use the following command

sudo yum install certbot python3-certbot-nginx

Once installed, we can use certbot to issue an SSL certificate for our custom domain.

certbot --nginx -d <DOMAIN_NAME>
👉
The --nginx flag allows the certbot to also update nginx configuration.

Certbot will automatically configure NGINX to use SSL, and you'll be able to test your website with HTTPS

Step 3: Configure NGINX

Now that all the tools and the packages have been installed, we can proceed ahead to configure the NGINX.

All the configuration files for nginx are located at the directory /etc/nginx/. But we are specifically interested in a particular directory - /etc/nginx/conf.d/. Any file at this path, gets loaded and parsed automatically.

So, if you've to add redirection for a domain name to a service running locally, say at port 1234, all you need to do is simply create a .conf file at that location

sudo nano /etc/nginx/conf.d/nginx.conf

and add the following configuration in the file

server {
    listen 80;
    listen [::]:80;
    root /var/www/html;
    server_name <DOMAIN_NAME>;
}

and then press CTRL + o followed by CTRL + x to save it.

Now, whenever you make changes to an nginx config file, you can test whether the configuration you added is syntactically correct or are there errors. For that, you can test for nginx syntax errors using the following cmd

sudo nginx -t

If everything is correct, it will print out the following on the terminal

Output of nginx configuration file test
Output of nginx configuration file test

And when you are sure, you can reload the nginx service for the changes to take effect, using

sudo nginx -s reload

To issue the TLS (or SSL) certificate, you can use the following command

sudo certbot --nginx -d <DOMAIN_NAME>

What this does is also add the following configuration to the nginx config file

Changed nginx config file afer SSL certificate issue
Changed nginx config file afer SSL certificate issue

As can be seen in the output above, certbot has created another separate server section here - one server section listens at the port numer 80 which also has a redirection to https part added, while the second server section serves all the https connection requests, encrypted with TLS. You might be thinking why even leave port 80 open for listening. It is because the TLS certificates issued by Let's encrypt are only issued for a period of 90 days. And after every 60 days, they are due to be renewed. The last 30 days is the grace period. And so, the certbot listens on port 80 only to renew the certificates. Rest all the traffic is served via the secure port 443.

So far, the SSL certificate is issued, but the redirection to the service is not yet added. So, lets add that last missing link as well.

All you need to do now is just add the redirection configuration to service in the server section which listens on port 443 , as shown below.

...
# other Config for server listening on port 443
    
    # Proxy pass to the service for local LAN IP
    location / {
        proxy_pass  http://127.0.0.1:<>;

        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;
    }

The final nginx configuration file should look like this.

Once this is done, make sure to restart the nginx server once again.

Now you should be able to reach your service from a domain name on the internet, served by encrypted connection over the https protocol.

Automatically Renew Let’s Encrypt Certificates

As mentioned above, Let’s Encrypt certificates expire after 90 days. So, lets add one small thing more, to make sure certbot can renew on its own.
Open the crontab using this command

crontab -e

Go to the very bottom of the file and add the following command to check daily and renew certs if any cert is to be renewed in 30 days.

0 12 * * * /usr/bin/certbot renew --quiet

Save and close the file. All installed certificates will be automatically renewed and reloaded.

Bonus tips

Notice here that you can add as many websites as you want.
For example, if there are three domain names you want to issue SSL certificates for, create a separate server section for each one of them.


server {
    listen 80 default_server;
    listen [::]:80 default_server;
    root /var/www/html;
    server_name abcd.com www.abcd.com;
}
server {
    listen 80 ;
    listen [::]:80 ;
    root /var/www/html;
    server_name efgh.com www.efgh.com;
}
server {
    listen 80 ;
    listen [::]:80 ;
    root /var/www/html;
    server_name ijkl.com www.ijkl.com;
}

Once done, test the nginx config and reload the nginx using

sudo nginx -t && sudo nginx -s reload

Certbot SSL cert generation

We can also generate all the certs in one command like this

sudo certbot --nginx -d abcd.com -d www.abcd.com \
  -d efgh.com -d www.efgh.com \
  -d ijkl.com -d www.ijkl.com

Conclusion

As you saw, if takes only a handful of commands and you can easily setup a redirection from a domain to a service you are running locally. This is also used almost everywhere in the industry. Millions of websites and domains use the SSL certificates issued by Let's encrypt and many of them use NGINX as their server and reverse-proxy. I hope this blog post has provided you useful information to setup things on your own.

If you have any questions, opinions or feedback to give, please feel free to write in the comments below.

Also, if you're interested in redirecting an incoming request from a sub-domain or redirecting to some end-point, We've written a detailed blog post here

Using NGINX to redirect to multiple websites
Planning on serving multiple websites using a couple of devices? Wondering how to redirect the incoming URL requests to multiple devices on LAN using a reverse-proxy like NGINX? Read on to find more…


Further Reading

  1. How to Install and setup Apache2
  2. How to Install and setup