Skip to main content
GullySystem
A practical guide

Nginx reverse proxy for a business app: a setup checklist

Ganesh HSStrategy and Technology, GullySystem
Practical advice · 3 min read

In this guide 6 sections
The short answer

A Node or PHP app needs seven things behind Nginx before launch. A proxy_pass block. Forwarded headers. An HTTPS redirect. WebSocket upgrade lines. An upload limit. Gzip for text. Cache rules that skip logged-in pages. Check each one with curl before you point DNS at the server, not in a browser.

The proxy block and forwarded headers

Point a location block at the app with proxy_pass to its local port, such as 127.0.0.1:3000. Keep the app bound to localhost. Nginx is then the only public door.

The app now sees Nginx as the client. Pass the truth along with proxy_set_header lines for Host, X-Real-IP, X-Forwarded-For and X-Forwarded-Proto. Then tell the framework to trust the proxy. Express uses trust proxy; Laravel uses TrustProxies.

  • Host header preserved
  • X-Forwarded-For for real client address
  • X-Forwarded-Proto for scheme
  • Framework told which proxy to trust

TLS and the HTTP redirect

Port 80 has one job. It sends a permanent 301 redirect to HTTPS. Port 443 holds the certificate, usually issued by Let’s Encrypt through certbot, with a systemd timer that renews it before expiry.

Check that the full chain is served, not only the leaf certificate. Add an HSTS header last. Browsers remember it, so every subdomain must already work over HTTPS.

WebSockets and long requests

Live dashboards, chat and socket.io need proxy_http_version set to 1.1 plus Upgrade and Connection headers. Without them the connection falls back to polling or drops silently.

Look at proxy_read_timeout as well. Report exports and slow API calls die at the default of sixty seconds. Raise it only on the location that needs it. Prefer a background job for work that runs for minutes.

  • Upgrade and Connection headers
  • HTTP version 1.1 upstream
  • Read timeout on export routes
  • Keepalive to the upstream

Upload limits, gzip and caching

Nginx rejects large bodies with a 413 error unless client_max_body_size is raised. Match it to what the app allows for invoices, photos or CSV imports, and raise it on the upload route alone.

Turn on gzip for HTML, CSS, JSON and JavaScript, never for images or PDFs that are already compressed. Serve hashed static files with long cache headers. Pages behind a login must send no-store, or one user’s dashboard may reach another.

Test before the cutover

Run nginx -t before every reload. Then use curl -I to confirm the redirect, the headers and the cache lines. Open the app on a phone over mobile data. That catches missing intermediate certificates.

Keep the previous config in version control and write the rollback command beside it. Read the error log for an hour after launch. A short list of 502 and 413 entries tells you what to tune next.

Reverse proxy launch checklist

Five blocks fill a single sheet: proxy and headers, TLS and redirects, WebSockets and timeouts, uploads and compression, caching and tests. Each line has a command to run, the expected result and a pass column. A footer holds the rollback command and error log path.

Open a blank worksheet to print

Questions owners ask

Why does my app think every visitor is on localhost?

Nginx is the client from the app’s view. Forward X-Forwarded-For and enable the framework’s trusted proxy setting so it reads the real address.

What causes a 502 after launch?

Nginx cannot reach the app. The process may be down, bound to another port, or crashing on start. Check the upstream address and the app log.

Do I need Nginx in front of Node?

It is not required, but it handles TLS, static files, compression and slow clients well, which frees the app for business logic.

Why do uploads fail with 413?

The body is larger than client_max_body_size. Raise it on the upload route and keep the app’s own limit consistent.

Browse all guidesBack to top ↑
Next step

Have a specific situation to work through?

This article covers the general case. Tell us what you’re actually dealing with and we’ll respond directly.

Share Your Requirements