VPS · Guide

nginx Reverse Proxy Guide

Updated October 11, 2026 5 min read

On this page
  1. What a reverse proxy does
  2. Step 1: Install nginx
  3. Step 2: Create the proxy server block
  4. The headers, and the trailing slash
  5. WebSockets
  6. Step 3: Free HTTPS with Certbot
  7. Fixing 502, 504 and 413 errors
  8. Run it on a HytHost VPS
  9. FAQ

A reverse proxy sits in front of your applications and passes visitors' requests to them. With nginx in front, a Node.js app, a Docker container or a dashboard running on some port becomes https://app.example.com, with HTTPS, proper headers and one place to manage it all.

This guide sets up nginx as a reverse proxy on Ubuntu, step by step: the server block, the headers, WebSockets, free HTTPS and the errors you are most likely to meet.

What a reverse proxy does

  • One public entry point. nginx listens on ports 80 and 443 and forwards each domain or path to the right app, so the apps themselves never face the internet.
  • HTTPS in one place. nginx handles the certificates; the apps behind it can speak plain HTTP on localhost.
  • Several apps on one server. app.example.com, api.example.com and example.com/blog can each go to a different service.
  • Extras: compression, caching, upload limits and access rules, without touching the app.

Step 1: Install nginx

sudo apt update
sudo apt install nginx

Open http://your-server-ip and you should see the nginx welcome page. If you use ufw, allow web traffic with sudo ufw allow 'Nginx Full'.

Make sure your app is running and reachable on the server itself, for example with curl http://127.0.0.1:3000. If that fails, nginx will fail too.

Run nginx and your apps with full root: HytHost KVM VPS plans start at €2.92/mo.

See VPS plans

Step 2: Create the proxy server block

On Ubuntu, each site gets a file in /etc/nginx/sites-available/, enabled with a link in sites-enabled. Create /etc/nginx/sites-available/app.example.com:

server {
    listen 80;
    server_name app.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;
    }
}

Enable it, test the configuration and reload:

sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Point the domain's A record at your server, and http://app.example.com now shows your app. Always run nginx -t before reloading: it catches typos before they take the site down.

The headers, and the trailing slash

  • Host passes the domain the visitor asked for. Without it, nginx sends the address from proxy_pass, and apps that build links from the host break.
  • X-Real-IP and X-Forwarded-For pass the visitor's IP address. Without them, every request looks like it came from 127.0.0.1.
  • X-Forwarded-Proto tells the app whether the visitor used HTTPS, so it does not redirect in a loop.

Ubuntu's nginx package ships these lines in /etc/nginx/proxy_params, so include proxy_params; is a shortcut.

The trailing slash matters. For an app under a path:

location /app/ {
    proxy_pass http://127.0.0.1:3000/;
}

With the slash after the port, /app/login reaches the app as /login. Without it, the app receives the full /app/login.

WebSockets

Chat apps, live dashboards and many admin panels use WebSockets, which need two extra headers. Add this map once, inside the http block, for example in a new file /etc/nginx/conf.d/websocket.conf:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Then in the location:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 1h;

nginx closes a proxied connection that sends nothing for 60 seconds; the longer proxy_read_timeout keeps quiet WebSockets open. proxy_http_version 1.1 is the default only from nginx 1.29.7, and Ubuntu 24.04 ships 1.24, so keep the line.

Step 3: Free HTTPS with Certbot

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/local/bin/certbot
sudo certbot --nginx -d app.example.com

Certbot gets a free Let's Encrypt certificate, adds the HTTPS settings to your server block and can redirect HTTP to HTTPS. It renews the certificate automatically; check with sudo certbot renew --dry-run. More on certificates in how to get a free SSL certificate.

Fixing 502, 504 and 413 errors

ErrorUsual causeFix
502 Bad Gatewaynginx cannot reach the app: it is stopped, crashed, or listens on another port or addressStart the app, check the port with curl http://127.0.0.1:PORT, read /var/log/nginx/error.log
504 Gateway TimeoutThe app took longer than 60 seconds to answerSpeed up the request, or raise proxy_read_timeout for that location
413 Request Entity Too LargeAn upload is bigger than nginx's default limit of 1 MBSet client_max_body_size 50m; in the server block
Redirect loopThe app does not know the visitor used HTTPSPass X-Forwarded-Proto and tell the app it is behind a proxy

After every change: sudo nginx -t, then sudo systemctl reload nginx.

Run it on a HytHost VPS

A reverse proxy needs a server where you control ports 80 and 443. HytHost VPS plans run on KVM with full root access, SSD storage and DDoS protection; pick Ubuntu 24.04 and follow the steps above. Running your apps in containers? Set up Docker first with how to install Docker on Ubuntu, and bind the containers' ports to 127.0.0.1 so only nginx can reach them.

FAQ

Frequently asked questions

A reverse proxy forwards requests to one or more apps behind it. A load balancer is a reverse proxy that spreads requests across several copies of the same app. nginx can do both, with an upstream block listing the copies.

nginx cannot reach the app behind it. Check that the app is running and listening on the address and port in proxy_pass, and read /var/log/nginx/error.log for the exact reason.

No, when the app runs on the same server. nginx handles HTTPS for visitors and talks to the app over plain HTTP on 127.0.0.1, which never leaves the machine.

Give each app its own server block with its own server_name, such as app.example.com and api.example.com, each with a proxy_pass to a different port.

Yes. Publish each container's port on 127.0.0.1 only and point proxy_pass at it. That keeps the containers off the public internet, which matters because Docker's published ports bypass ufw.

Host your apps behind nginx

HytHost KVM VPS plans with full root access, SSD storage and DDoS protection start at €2.92/mo.

Live chat 8 AM – 11 PM (Chișinău time) · tickets answered during business hours

Was this guide helpful?