How to Set Up Nginx as a Reverse Proxy
An Nginx reverse proxy accepts requests on ports 80 and 443 and forwards them to an application on a local port (for example 127.0.0.1:3000), adding the headers the backend needs. That gives you a single entry point, HTTPS and load balancing. Below is a working configuration with proxying, headers, TLS and WebSocket support.
What a reverse proxy does
- Accepts external HTTP/HTTPS requests and passes them to a backend (Node.js, Python, PHP-FPM and so on).
- Terminates TLS: the application speaks plain HTTP while Nginx handles encryption.
- Hides the internal structure and application ports behind a single domain.
- Lets you balance load across several backends.
Step 1. Install Nginx
sudo apt update
sudo apt install nginx
sudo systemctl enable --now nginx
Step 2. Create the site config
Create a virtual host file:
sudo nano /etc/nginx/sites-available/app.conf
A basic reverse proxy to an application on port 3000:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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 the headers matter: without X-Forwarded-For and X-Real-IP the application sees the IP 127.0.0.1 instead of the real client, and X-Forwarded-Proto is what tells the backend the original request was HTTPS.
Step 3. Enable the config
sudo ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
The nginx -t command validates the syntax before the reload — do not skip it.
Step 4. WebSocket support
If the application uses WebSocket (chats, live updates), add the Upgrade headers to the location block:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Step 5. Enable HTTPS
Install certbot and get a Let’s Encrypt certificate — it writes the TLS settings into the config for you:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com
After that Nginx listens on 443 with the certificate, and requests on port 80 are redirected to HTTPS.
Balancing several backends
To spread the load, describe an upstream group:
upstream backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Timeouts and upload size
By default Nginx caps the request body at 1 MB and cuts off slow backend responses. For applications with file uploads and long-running operations, raise the limits in the server or location block:
client_max_body_size 50m;
proxy_connect_timeout 60s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
The client_max_body_size directive sets the maximum upload size, while the proxy_* timeouts control how long Nginx waits for the backend to connect and respond before returning 504 Gateway Timeout.
Proxying by path
A single domain can be split between several applications by URL prefix. For example, /api/ goes to the backend and everything else to the frontend:
location /api/ {
proxy_pass http://127.0.0.1:4000/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
proxy_pass http://127.0.0.1:3000;
}
Note the trailing slash in proxy_pass http://127.0.0.1:4000/ — it strips the /api/ prefix, so the backend receives the path without it.
Key directives
| Directive | Purpose |
|---|---|
proxy_pass |
backend address |
proxy_set_header |
headers passed to the application |
proxy_http_version 1.1 |
required for keep-alive and WebSocket |
upstream |
group of servers for balancing |
Frequently asked questions
What causes a 502 Bad Gateway?
Nginx could not reach the backend: the application is not running, listens on a different port, or crashed. Check ss -tulpn | grep 3000 and the application logs.
The app sees 127.0.0.1 instead of the client IP — why?
The headers are not being forwarded. Add X-Forwarded-For and X-Real-IP, and configure the application to trust them.
How do I test the config before applying it?
Run sudo nginx -t — it reports syntax errors without reloading the service.
Do I need to expose the backend port to the internet?
No. The backend listens on 127.0.0.1 and only Nginx’s ports 80 and 443 are open. That is the safer setup.
How is the TLS certificate renewed?
certbot installs an auto-renewal timer. Test it with sudo certbot renew --dry-run.
Need a server to host Nginx and your applications? Deploy a Linux VPS in the VPS hosting section or order the setup through server administration.