Server Management

Nginx Reverse Proxy Setup: A Step-by-Step Guide

ahmet
5 min read
#Nginx#Reverse Proxy#Server
Nginx Reverse Proxy Setup: A Step-by-Step Guide
Learn nginx reverse proxy setup step by step with proxy_pass, upstream, and SSL termination examples. Practical config in front of Gunicorn and Node.

Your app runs on port 8000 but nobody can reach it from a browser. You also have no idea where to put your SSL certificate. An nginx reverse proxy solves both of these problems.A reverse proxy is a layer that sits between the client and your application server. It takes incoming requests, forwards them to the backend app, and returns the response. Instead of exposing your Gunicorn or Node app directly to the internet, you place it behind this layer.This guide walks through the setup from scratch with working examples. By the end you will have a backend that is hidden and served over HTTPS.

Why do you need a reverse proxy?

Application servers like Gunicorn and Node are good at handling requests. But serving static files, holding many connections, and managing SSL are not their job. This is exactly where nginx steps in.An nginx reverse proxy gives you these benefits:

  • Collecting all traffic through a single port 443
  • Managing the SSL certificate in one place
  • Serving static files without touching the app
  • Spreading load across multiple app instances
  • Hiding backend ports from the outside world

The official Nginx reverse proxy documentation explains all of these behaviors in detail. The core logic is simple: nginx takes the request, passes it to the backend, and returns the response to the client.

Installing the nginx reverse proxy and first config

On an Ubuntu-based server the install is one step. Install the package, start the service, check its status.

sudo apt update
sudo apt install nginx
sudo systemctl enable --now nginx

You keep configuration files in the /etc/nginx/sites-available/ directory. Creating a separate file for each site is a clean habit. After writing the file, you link it into sites-enabled with a symbolic link.Test the configuration after every change. This step keeps a typo from crashing the service.

sudo nginx -t
sudo systemctl reload nginx

Basic routing with proxy_pass

The heart of the job is the proxy_pass directive. Inside a location block it forwards the incoming request to the backend. Let us assume Gunicorn listens on port 8000.

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        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;
    }
}

Why do the proxy_set_header lines matter? Your backend app learns the real client address and protocol from these headers. Skipping them makes everyone's IP show up as 127.0.0.1 in your logs.For a Node app the only difference is the port number. Writing proxy_pass http://127.0.0.1:3000; is enough. The logic stays the same.

Upstream blocks and load balancing

A single app instance is sometimes not enough. When traffic grows, you want to run several instances and share the load. This is where the upstream block comes in.

upstream app_pool {
    server 127.0.0.1:8000;
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
}

server {
    listen 80;
    server_name example.com;

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

Nginx distributes requests across the servers in this pool in turn. If one instance crashes, it routes traffic to the others. Running instances on separate ports, rather than only raising the Gunicorn worker count, is a more flexible way to scale.The table below sums up the common load balancing methods.


MethodBehaviorWhen to use
round-robin | Distributes requests in turn | Default, equally powerful servers
least_conn | Sends to the one with fewest connections | Long-running requests
ip_hash | Same IP always hits the same server | Where session stickiness is needed

Setting up SSL termination

SSL termination means handling HTTPS encryption at the nginx layer. The client talks to nginx over an encrypted channel, and nginx talks to the backend over plain HTTP. Your backend server never deals with the encryption load.After you obtain the certificate, the config looks like this:

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/ssl/certs/example.crt;
    ssl_certificate_key /etc/ssl/private/example.key;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

The second server block redirects anything hitting port 80 to 443. That way every visitor moves to HTTPS automatically. For a free certificate, the Certbot tool can handle all of these steps for you.

Common errors and their fixes

The most frequent error on a first setup is 502 Bad Gateway. It means nginx could not reach the backend. First check whether the application is actually running.

  1. Verify the backend listens on the correct port.
  2. Check the port number in the proxy_pass address.
  3. Look for typos with sudo nginx -t.
  4. Read /var/log/nginx/error.log for the error trail.

If connections drop on apps that use WebSocket, you need to add the upgrade headers. Nginx does not forward these headers by default. The proxy_set_header Upgrade and proxy_set_header Connection lines fix the issue.

Summary and next step

An nginx reverse proxy is the most solid way to expose your application to the internet. You route with proxy_pass, scale with upstream, and secure with SSL termination. Your backend ports stay hidden, and your traffic passes through a single door.This setup needs a solid server foundation. At Kritm Cloud Solutions, our Turkey-based cloud infrastructure with NVMe disks and guaranteed CPU/RAM is built for exactly this kind of work. Take a look at our cloud solutions, or get in touch for setup support.