natbox.io

Guides / nginx reverse proxy

Run several apps behind an nginx reverse proxy

Last updated 3 November 2026 · By Mohammad AlSaraf

About 15 minutes. Debian and Ubuntu. For dashboards, APIs and web apps that should be on HTTPS without a port number.

A reverse proxy takes every request on ports 80 and 443 and passes it to the right app, by domain name or by path. The apps themselves listen only on the server, so nobody can reach them directly, and you get one place for HTTPS.

1. Plan the names

Choose a subdomain for each app, for example app.example.com and api.example.com. Link each one in the Domains tab of the client area, with the A and TXT records it shows, and press Verify (how domains work). Every subdomain counts as one domain: 3 on Nano, 10 on Box, 25 on Box Plus and Box CPU. Short of domains? Use paths instead, shown in step 5.

2. Keep the apps private

Make each app listen on 127.0.0.1 and a port of your choice. These ports do not need to be in your forwarded block, because only nginx talks to them. For example:

uvicorn main:app --host 127.0.0.1 --port 8000    # api.example.com
PORT=3000 HOST=127.0.0.1 node server.js           # app.example.com
Why 127.0.0.1 matters
An app listening on all addresses is also reachable on your public IPv6 addresses, on its own port, without HTTPS. Binding it to 127.0.0.1 keeps it behind the proxy.

3. Install nginx and add the sites

apt update && apt install -y nginx certbot python3-certbot-nginx

Save as /etc/nginx/sites-available/apps:

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

server {
    listen 80;
    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-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;          # WebSockets
        proxy_set_header Connection $connection_upgrade;
    }
}

server {
    listen 80;
    listen [::]:80;
    server_name api.example.com;
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
ln -s /etc/nginx/sites-available/apps /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

4. Turn on HTTPS

certbot --nginx -d app.example.com -d api.example.com

Certbot gets free Let's Encrypt certificates, adds the HTTPS settings and renews them by itself. It works because port 80 for your linked domains reaches your server. Visit https://app.example.com to check.

5. One domain, several apps by path

If you would rather use one name, send paths to different apps inside one server block:

location /api/ {
    proxy_pass http://127.0.0.1:8000/;   # the trailing / strips /api from the path
    proxy_set_header Host $host;
}
location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
}

Some apps expect to live at the root of a domain. If links or assets break under a path, look for a "base URL" or "root path" setting in the app, or give it its own subdomain.

Good to know

More guides: Run PostgreSQL on a small server, Host a Python API with FastAPI, Keep an app running with systemd.