Skip to content

Real client IP, path base and Host through a reverse proxy

Verified 1 min read

Forward X-Forwarded-For/Proto/Prefix and Host correctly, trust only the proxy, and stop redirect loops and wrong absolute URLs.

Tested onStandard / Datacenter · Build 26100.33650 · x64
E1
Build
26100.33650
Edition
Standard / Datacenter
Architecture
x64
Support
Documentation (E1)
Evidence
E1Official documentation
Verified
2026-10-29
Page type
Task

A reverse proxy must tell the app:

  • who the client is: X-Forwarded-For (or RFC 7239 Forwarded);
  • which scheme: X-Forwarded-Proto;
  • which host: preserve Host;
  • which path prefix: X-Forwarded-Prefix when the app is mounted under /app.

And the app must trust these headers only from the proxy.

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;
proxy_set_header X-Forwarded-Host $host;
}

For a path-mounted app:

location /app/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header X-Forwarded-Prefix /app;
}

The trailing slash on proxy_pass strips /app/; the prefix header tells the app how to generate links.

  • Preserve Host in the farm/proxy settings.
  • Forward X-Forwarded-For/X-Forwarded-Proto.
  • If the app is under a path, set X-Forwarded-Prefix via URL Rewrite server variables.
  • Do not rewrite Host to localhost unless the app is configured for it.

Express:

app.set('trust proxy', 1);

ASP.NET Core:

app.UseForwardedHeaders(new ForwardedHeadersOptions {
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

Configure known proxies/networks; the default often trusts only loopback. Do not trust all proxies on a public host.

Symptom: browser bounces between http and https, or redirects to localhost.

Causes:

  • proxy sends X-Forwarded-Proto: http after TLS termination;
  • app ignores the header and redirects to HTTPS;
  • proxy rewrites Host to an internal name;
  • canonical-host rule catches the health check.

Fix the header/trust boundary, not by disabling HTTPS redirects globally.

Terminal window
curl.exe -I -H "X-Forwarded-Proto: https" https://lab.example/
curl.exe -H "Host: app.example" http://127.0.0.1:3000/whoami

The app should report the public scheme/host and the real client IP, and redirects should use the public name.

E1. The lab ran Nginx/IIS without a multi-hop proxy; the headers are the configuration to validate with a real upstream on Server 2025.

Sources