Guides / nginx reverse proxy
Run several apps behind an nginx reverse proxy
Last updated 3 November 2026 · By Mohammad AlSaraf
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
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
- Your apps see the visitor's address in the
X-Forwarded-Forheader. - HTTPS reaches your server untouched: we route it by name and never decrypt it, so the certificates are yours.
- Prefer automatic HTTPS with less configuration? Caddy does the same job in a few lines.
- Static sites need no proxy: see host multiple websites.
More guides: Run PostgreSQL on a small server, Host a Python API with FastAPI, Keep an app running with systemd.