When a browser requests your app, which process actually handles the request? In many deployments, it’s Nginx at the public edge—and an application process behind it doing the app-specific work.
That distinction helps explain what Nginx is for, why a server might have both Nginx and Node or Python running, and where to look when a request fails.
The request path is the useful mental model
Nginx is software that can serve web content, act as a reverse proxy, distribute requests across app instances, and handle TLS. Those are different jobs it can perform, sometimes together in one configuration.
A common setup looks like this:
Browser → Nginx → application
Nginx accepts public HTTP or HTTPS requests, then forwards matching requests to an application that might listen on an internal address such as 127.0.0.1:3000. The application processes the request, and Nginx relays the response to the browser.
The app doesn’t have to be exposed directly to the internet. Nginx provides a stable public-facing layer, while the app can be restarted or moved without necessarily changing that public entry point. This separation is useful, but it doesn’t guarantee uptime: if the app is unavailable, Nginx can still return an error.
Nginx can serve files without an app
For a static site, Nginx can read files from disk and return them directly. The response might be an HTML page, a JavaScript bundle, a stylesheet, or an image. No application code needs to run for those requests.
Browser → Nginx → files on disk
This can also be combined with a backend: Nginx serves static assets itself and forwards other requests to an application. Static asset requests then don’t need to reach the app process.
What reverse proxying looks like
Here’s a minimal example that forwards requests to an app listening on port 3000 on the same machine:
server {
listen 80;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
In this configuration, Nginx listens on port 80. The location / block matches requests under /, and proxy_pass sends those requests to the local service on port 3000. Nginx returns the upstream response to the client.
This is a conceptual example, not a production-ready configuration. A real deployment may also need to pass request details such as the original host and client address, configure HTTPS, and account for timeouts and buffering. The exact requirements depend on the app and deployment.
Nginx is not running the application code here. It’s forwarding requests; the backend is where application logic, database queries, and other app work happen.
Why put Nginx in front?
A reverse proxy is an architectural boundary, not a fix for an application that can’t handle traffic. It can give you:
- A stable public endpoint: the public-facing HTTP configuration can stay in place while an app is redeployed or moved.
- A place for HTTP and TLS settings: redirects, certificates, and related rules can be handled at the edge rather than separately in every app.
- Direct static-file serving: assets can be returned without involving the backend.
- Request distribution: Nginx can distribute requests across multiple instances of an application.
If you’re deciding whether to put traffic across multiple backends, the Nginx load-balancing patterns and trade-offs are a useful next step.
These benefits don’t mean Nginx makes an app automatically secure or scalable. It doesn’t fix insecure application code, and it can’t serve a successful response from a backend that’s down.
Nginx and application servers do different jobs
It’s easy to treat Nginx and an application server as competing choices, but in a reverse-proxy setup they have different responsibilities:
| Component | Typical responsibility |
|---|---|
| Nginx | Accept public HTTP/TLS traffic, route requests, and optionally serve files |
| Application server or runtime | Execute application code and produce app responses |
There’s an important variation for PHP: Nginx doesn’t execute PHP itself. It commonly passes PHP requests to PHP-FPM using FastCGI, rather than proxying to it as an HTTP backend with proxy_pass.
A response error doesn’t tell you which layer failed
A failure could come from Nginx or from the upstream application. Nginx might generate an error because of its own configuration, or it might report that it couldn’t get a usable response from the backend. Start by identifying which layer produced the response, then check the relevant configuration, service, and logs. For an upstream connection failure, this guide to diagnosing Nginx 502 errors walks through common causes.
Before applying a configuration change, test its syntax with:
nginx -t
A passing syntax test doesn’t prove that every upstream service is reachable or that the behavior is correct, but it can catch configuration errors before you proceed.
Do you need Nginx?
Not always. A static hosting platform or CDN may already serve files and handle the public HTTP layer. A small app may not need a separate proxy at all. If you need a dedicated layer for TLS, routing, static assets, or multiple backends, Nginx is one option; other reverse proxies and managed load-balancing services can fill a similar role.
The key question is what sits on each side of the request. If Nginx serves the file, it’s acting as a web server. If it forwards the request, it’s acting as a reverse proxy. Knowing which job it’s doing makes both architecture decisions and troubleshooting clearer.
I originally published a more detailed version of this guide on the SSHFlow blog.
I'm also building SSHFlow — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.
Top comments (0)