NAWAZ SHARIF
October 17, 2026 · Web Server · By Nawaz Sharif

Nginx Reverse Proxy Explained: Setup, HTTPS, Load Balancing and Security

When you run a web application, you rarely want users to connect to it directly. Your app may listen on a private port, it may not handle HTTPS well, and you may want to run more than one copy of it. A reverse proxy solves all of this. Nginx is the most widely used reverse proxy, and this guide shows how it works and how to set it up step by step on Ubuntu or Debian.

What is a reverse proxy?

A forward proxy sits in front of clients and hides them from the internet, like a corporate proxy. A reverse proxy does the opposite: it sits in front of your servers. Visitors connect only to the proxy, and the proxy forwards each request to the right backend and sends the answer back. The visitor never sees your backend servers.

The diagram below shows the flow.

Nginx reverse proxy diagram A client browser sends an HTTPS request over the internet to Nginx on your server. Nginx forwards the request over HTTP to one of two private backend application servers and returns the response to the client. Your server Client browser HTTPS Nginx reverse proxy TLS, caching App 1 port 3000 App 2 port 3001 HTTP HTTP Only ports 80 and 443 are public; backend ports stay private
Figure 1: Nginx as a reverse proxy. Clients talk only to Nginx, which forwards requests to private backend servers.

How a request travels

Follow one visit step by step. The browser looks up your domain name and connects to your server on port 443. Nginx decrypts the HTTPS traffic, reads the host name and the path, and decides which backend should handle the request. It then opens a separate plain HTTP connection to that backend and waits for the answer. When the backend replies, Nginx passes the response back to the browser, optionally adding headers or caching it. To the visitor it looks like one single website, even when several servers are working behind it.

Why use a reverse proxy?

Step 1: Install Nginx

sudo apt update
sudo apt install nginx
sudo systemctl status nginx

If the service is active, opening your server's IP address in a browser shows the Nginx welcome page.

Step 2: Create the proxy configuration

Assume your application is running on port 3000 on the same machine. Create a file called /etc/nginx/sites-available/myapp:

server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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 proxy_pass line tells Nginx where to send requests. The proxy_set_header lines pass useful information to your app. Without them, the backend would see every visitor as coming from 127.0.0.1 and would not know the original host name or whether the visitor used HTTPS.

Step 3: Enable the site and reload

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Always run nginx -t before reloading. It checks the configuration for mistakes, so a typo does not take your site down. If the default welcome page still appears, remove the default site link from sites-enabled.

Step 4: Add free HTTPS with Let's Encrypt

Certbot can obtain a certificate and update the Nginx configuration for you. Your domain must already point to the server.

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run

The last command confirms that automatic renewal works. Certificates are valid for a limited time, and certbot renews them in the background.

Step 5: Load balancing across several servers

To run two copies of your app, define an upstream group and point proxy_pass at its name:

upstream myapp {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://myapp;
        proxy_set_header Host $host;
    }
}

By default Nginx shares requests between the servers in turn (round robin). Adding least_conn; inside the upstream block sends each request to the server with the fewest active connections, and Nginx temporarily skips a server that stops responding.

Serving static files

Letting Nginx serve images, CSS and JavaScript directly takes load off your application:

location /static/ {
    root /var/www/myapp;
    expires 7d;
}

Security basics

The server itself needs protection too, so follow my guide on securing Linux servers and log in with SSH keys instead of passwords.

Troubleshooting common errors

502 Bad Gateway usually means the backend is not running or proxy_pass points to the wrong port. Test it directly with curl -I http://127.0.0.1:3000. 504 Gateway Timeout means the backend answered too slowly. For anything else, read the error log with sudo tail -f /var/log/nginx/error.log, and check the service with sudo journalctl -u nginx -n 50. If you need a refresher on these commands, see my guide to essential Linux commands.

Summary

A reverse proxy gives your application a single, secure front door. Install Nginx, forward traffic with proxy_pass, add HTTPS with certbot, and use an upstream group when you need more than one backend. It also pairs well with containers, so you may want to read my Docker for beginners guide next.

← All articles