Skip to main content
To set up Nginx as a reverse proxy, add a server block with location / { proxy_pass http://127.0.0.1:3000; } plus the Host, X-Real-IP, X-Forwarded-For and X-Forwarded-Proto headers, then run sudo nginx -t and sudo systemctl reload nginx. Your app keeps listening only on 127.0.0.1, and Nginx serves it on ports 80 and 443 with your domain and HTTPS.

Before you start

  • Install Nginx and point your domain at the server (point a domain).
  • Start your app and check it answers locally, for example curl http://127.0.0.1:3000. This guide uses port 3000 and the domain app.example.com.
  • Use a user with sudo. If you are root, leave out sudo.
A reverse proxy sits in front of your app. Visitors talk to Nginx; Nginx forwards each request to the app and returns the answer. This gives you one place for HTTPS, domains, logs and limits, and your app port is never exposed.

How to configure the reverse proxy

1

Create the server block

Ubuntu / Debian: /etc/nginx/sites-available/app.example.com. AlmaLinux / Rocky: /etc/nginx/conf.d/app.example.com.conf.
The map block and the Upgrade / Connection headers let WebSocket connections through. Define the map only once across all your config files.
2

Enable the site (Ubuntu / Debian only)

3

Allow Nginx to connect to the app (AlmaLinux / Rocky only)

SELinux blocks Nginx from opening network connections by default. Allow it:
4

Test and reload

What each line does

How to check it worked

You should get your app’s response, the same as curl http://127.0.0.1:3000. Then open http://app.example.com in a browser. If you see 502 Bad Gateway, Nginx could not reach the app. The error log names the reason, for example connect() failed (111: Connection refused) while connecting to upstream. See fix 502 Bad Gateway.

How to add HTTPS

Run Certbot for the proxied domain:
Certbot adds the listen 443 ssl lines and the certificate to this server block. See free SSL with Certbot.
Make your app listen on 127.0.0.1, not 0.0.0.0, and do not open its port (such as 3000) in your firewall. Otherwise visitors can bypass Nginx and HTTPS.

How to proxy long requests or large uploads

Nginx closes a proxied request if the app sends nothing for 60 seconds (proxy_read_timeout) and refuses request bodies over 1 MB (client_max_body_size). Raise them only where needed:

Frequently asked questions

My app still logs 127.0.0.1 as the client IP. Why?

The app must be told to trust the proxy and read X-Forwarded-For. In Express, set app.set('trust proxy', 'loopback'); in Django or Flask, use the framework’s proxy settings. Only trust the header from 127.0.0.1.

Can I proxy a sub-path like example.com/app/?

Yes: location /app/ { proxy_pass http://127.0.0.1:3000/; }. The trailing slash on proxy_pass strips /app/ before the request reaches the app. Many apps also need a base-path setting.

Can Nginx balance across several app instances?

Yes. Define upstream app_servers { server 127.0.0.1:3000; server 127.0.0.1:3001; } in the http context and use proxy_pass http://app_servers;.

Is Caddy easier for this?

Caddy needs one line, reverse_proxy 127.0.0.1:3000, and adds HTTPS by itself. See install Caddy.

VPS and dedicated server guides

Every server guide in one place.

Node.js with PM2

Run the app behind the proxy.

Python with Gunicorn

Flask and Django behind Nginx.

Fix 502 Bad Gateway

When the upstream fails.

Free SSL with Certbot

HTTPS for your proxy.

Checked against the official nginx proxy module documentation, nginx WebSocket proxying guide and Red Hat Enterprise Linux 9 Nginx guide on 4 November 2026; proxy configuration tested on Ubuntu 24.04 LTS (Nginx 1.24.0) with a Node.js app. Commands can differ on other distributions or releases.

Need Extra Help?

If you encounter any issues, our support team is ready to assist:

Save on Your Hosting

Ready to upgrade your hosting? Use code KB20 at checkout for 20% off your first order!

Last Updated: November 2026