Reverse proxy applications

Forward client request details

Pass the original host, client address, scheme, and request identifiers through a trusted proxy boundary.

8 minute lesson

~~~

The upstream sees Nginx as its direct client unless Nginx forwards selected request metadata. Without help, your application thinks every visitor is 127.0.0.1 making plain HTTP requests to the wrong hostname. Anything that depends on the real client — logging, rate limits, redirect URLs, “secure cookie” decisions — breaks quietly.

proxy_set_header sets the headers Nginx sends upstream. This is the standard set:

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;
}

Host carries the hostname the visitor actually requested, so the application builds correct absolute URLs. X-Real-IP is the address of the TCP connection Nginx accepted. X-Forwarded-For is the hop chain: $proxy_add_x_forwarded_for takes any incoming X-Forwarded-For and appends $remote_addr to it. X-Forwarded-Proto says whether the visitor used https, which the app needs for secure cookies and redirect schemes since the proxy leg is often plain HTTP.

The trust problem

Any client can send these headers themselves. Watch:

curl http://app.example.com/whoami -H "X-Forwarded-For: 1.2.3.4"

With $proxy_add_x_forwarded_for, the upstream receives X-Forwarded-For: 1.2.3.4, 203.0.113.50 — the spoofed value survives as the first entry, followed by the real address Nginx observed. That’s fine only if the application knows to trust the last hop, not the first.

So the rule is: the application should trust forwarding headers only from known proxies, and only as many hops deep as your real topology. If Nginx is the sole proxy and you want zero ambiguity, replace untrusted inbound values at the edge instead of appending:

proxy_set_header X-Forwarded-For $remote_addr;

Now whatever the client claimed is discarded and the upstream sees exactly one trustworthy address.

One Nginx-specific pitfall: proxy_set_header directives don’t merge across contexts. If a location defines even one of its own, it stops inheriting all the ones set at the server or http level. Adding a single header in a nested location can silently drop Host and X-Forwarded-Proto. Keep the full set together, or repeat it.

Make your test application print the headers it receives. Send spoofed forwarding headers from curl and verify the application receives only the policy Nginx intends.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →