Configure Nginx as a reverse proxy
A reverse proxy puts Nginx in front of your application: visitors talk to Nginx on port 80 or 443, and Nginx forwards the request to a process listening locally. The application is no longer exposed directly, certificates and logs are centralised, and you can change the internal port without touching DNS.
Prepare the application
The application must listen on loopback, for example 127.0.0.1:3000, not on 0.0.0.0. Check before configuring Nginx:
curl -I http://127.0.0.1:3000
sudo ss -tlnp 'sport = :3000'An app bound to 127.0.0.1 is intentionally unreachable from the Internet. The domain must already point to the VPS with an A or AAAA record before testing the proxy.
Create the server block
Create /etc/nginx/sites-available/app.votre-domaine.fr:
server {
listen 80;
listen [::]:80;
server_name app.votre-domaine.fr;
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;
}
}Enable it:
sudo ln -s /etc/nginx/sites-available/app.votre-domaine.fr /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxHost keeps the requested name, which the application uses to generate URLs. X-Real-IP and X-Forwarded-For pass the client address for logs and rate limiting. X-Forwarded-Proto tells the original scheme, useful when TLS terminates at Nginx.
WebSocket, timeouts and size
For an application using WebSocket, add a map table in the http context, for example in /etc/nginx/conf.d/upgrade.conf:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}Then, in the location block:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;Without these headers, the WebSocket handshake fails and the connection falls back to polling or closes. proxy_read_timeout defaults to 60 seconds: an idle connection longer than that is cut. For streaming or long requests, raise it and consider proxy_buffering off;.
For uploads, client_max_body_size 10m; prevents a 413 Request Entity Too Large. Put it in the server or location block.
Test and diagnose
sudo nginx -t
sudo systemctl reload nginx
curl -I -H 'Host: app.votre-domaine.fr' http://127.0.0.1
curl -I https://app.votre-domaine.fr502 Bad Gateway: the application is not running, listens on another port or another interface.504 Gateway Timeout: the application exceedsproxy_read_timeout.413: request body too large.404: wronglocationor root, not a proxy problem.
Check /var/log/nginx/error.log: Nginx reports the port it contacted and the exact error. Also verify that the application reads X-Forwarded-* headers correctly and does not blindly trust X-Forwarded-For coming from the Internet.
- Keep the application on
127.0.0.1and never open its port in a firewall. - Terminate TLS with Certbot on Nginx, then redirect HTTP to HTTPS.
- Add a health route and watch 5xx codes.
- After every change:
nginx -t, reload, then a real test.
Verify
sudo nginx -T | grep -A5 'server_name app.votre-domaine.fr'
curl -s -o /dev/null -w '%{http_code}\n' https://app.votre-domaine.frThe code must be 200, 301 or 302, never 502. If you changed the internal port, confirm with ss -tlnp that the application still listens and that proxy_pass points to the right port.
Advertisement
Was this article helpful?
Read next
- Install Nginx on DebianInstall Nginx, check the service, test locally, open the firewall and inspect the logs.
- Host multiple sites with NginxServe several domains on one VPS with separate server blocks, distinct roots and a controlled reload.
- Manage Linux users and groupsCreate an account, add it to the right group, manage service accounts and audit access.