# Reverse Proxy in Nginx: proxy_pass and Backend Headers

> Reverse proxy in Nginx: how to configure proxy_pass, forward client headers to the backend, and keep the visitor IP intact.

## What a reverse proxy in Nginx is and why you need one

A reverse proxy is a server that accepts requests from clients and forwards them to one or more backends: an application on Node.js, Python, Java, or another Nginx instance. The client only talks to the proxy and never sees the internal structure of the service. Nginx can act as a reverse proxy out of the box — all it takes is the `proxy_pass` directive inside a `location` block.

A reverse proxy in Nginx solves several tasks at once: it terminates TLS in front of the application, serves static files directly bypassing a slow backend, hides internal ports and IP addresses, and adds security headers. On a ZevsHost.net VDS this setup is often placed in front of a Node.js or Python application: Nginx listens on port 80 and 443, while the service itself runs on localhost and is never exposed directly.

- Load balancing across several copies of the application.
- A single SSL certificate covering the whole set of internal services.
- Caching backend responses without touching the application code.

## Configuring proxy\_pass: a basic example

The minimal config is a `server` block with one `location` that forwards every request to the local application over HTTP.

```
server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}
```

If `location` ends with a trailing slash and `proxy_pass` includes a path, Nginx replaces the matched part of the URI with that path. Without a path in `proxy_pass`, the original URI is passed to the backend as is — the most predictable option, especially when the application does its own routing. For matching rules in detail, see the article on [location priority](https://www.zevshost.net/en/kb/nginx-location-prioritet/).

## Forwarding client headers to the backend

By default Nginx replaces the `Host` header with the backend address and never tells the application the visitor's real IP. If this is left unfixed, application logs will only show the Nginx IP, and links in emails or redirects may point to localhost instead of the real domain.

```
location / {
    proxy_pass http://127.0.0.1:3000;
    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;
}
```

The backend application should trust these headers only when a request truly comes from the local Nginx — otherwise a client could forge X-Forwarded-For directly.

## Timeouts and buffers: what each parameter controls

If the backend responds slowly or returns a large response, Nginx default timeouts and buffers can cut the connection off too early. The values below are a working starting point for an ordinary web application, not for streaming.

| Parameter | Purpose | Typical value |
| --- | --- | --- |
| proxy\_connect\_timeout | waiting for the TCP connection to the backend | 5s |
| proxy\_send\_timeout | time between writes of data to the backend | 60s |
| proxy\_read\_timeout | time between reads of the backend response | 60s |
| proxy\_buffer\_size | buffer for the first part of the response (headers) | 4k |
| proxy\_buffers | number and size of buffers for the response body | 8 4k |

A too short `proxy_read_timeout` is a common cause of 504 Gateway Timeout on heavy requests such as exporting a report or uploading a file. Buffers that are too small force Nginx to write temporary files to disk, which is noticeable on a VDS with a slow disk.

## Reverse proxy to several servers through upstream

If the application runs in several copies, instead of a single address in `proxy_pass` you use an `upstream` block with a list of servers.

```
upstream backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002 backup;
}

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://backend;
    }
}
```

By default Nginx distributes requests round robin and skips the server marked `backup` while the others are alive. For load distribution algorithms and backend health checks, see the separate article on the [Nginx load balancer](https://www.zevshost.net/en/kb/nginx-load-balancer/). If HTTPS is also needed in front of the reverse proxy, the certificate is configured on the outer server — see the article on [SSL/TLS in Nginx](https://www.zevshost.net/en/kb/nginx-ssl-tls-nastroika/).

## Checklist: reverse proxy before production

- The Host, X-Real-IP, and X-Forwarded-For headers are forwarded to the backend and visible in its logs.
- The `nginx -t` command passes without errors after every config change.
- Timeouts are tuned to the application's real response time instead of being left at defaults.
- With several backends in upstream, at least one backup server is configured.
- The response has been checked with `curl -I http://app.example.com/` — headers and status code match expectations.

## Frequently asked questions

### What happens if you do not forward the X-Real-IP and X-Forwarded-For headers to the backend?

If the Host, X-Real-IP, and X-Forwarded-For headers are not forwarded, the backend application will only see the Nginx address in its logs, not the visitor's real IP. As a result, links in emails and redirects may point to an internal address instead of the real domain, and any logic tied to the client's IP stops working correctly.

### Can the X-Forwarded-For header sent by the client always be trusted?

No, that header should not be trusted without limits: if a request reaches the backend directly rather than through the local Nginx, a client can set X-Forwarded-For to anything. An application should treat the header as reliable only when it is certain the request actually passed through its own proxy rather than arriving straight from an outside source.

### Why does a 504 Gateway Timeout sometimes appear when going through a reverse proxy?

A 504 usually happens when the backend's response takes longer than proxy\_read\_timeout allows, for example while exporting a heavy report or uploading a large file. Nginx closes the connection before the application manages to respond. The fix is to raise just that timeout for slow requests, leaving the rest of the proxy settings untouched.

### Do the typical reverse proxy timeout and buffer values work for streaming video or large files?

No, the values given are a starting point for ordinary web requests, not for streaming. Streaming video or large files calls for a different approach to buffers and timeouts; otherwise the connection may be cut off, or Nginx may start writing temporary files to disk, which is especially noticeable on a server with a slow disk.

---

Source: https://www.zevshost.net/en/kb/nginx-reverse-proxy/
