Azure & Cloud Architecture

Forwarded Headers in ASP.NET Core Behind a Reverse Proxy

Forwarded headers are security-sensitive claims about the original request. Trust the wrong hop and clients can forge scheme, host or address; trust none and production generates broken redirects and cookies.

Reverse proxy forwarded-header flow

The application sees the last hop, not the browser

TLS often terminates at Azure Front Door, Application Gateway, a load balancer or ingress proxy. The proxy then calls Kestrel over a private HTTP connection. Without trusted forwarding information, ASP.NET Core sees http, the proxy address and an internal host even though the browser used public HTTPS.

That mismatch affects redirects, absolute links, OpenID Connect callback URIs, secure-cookie decisions, canonical URLs, rate-limit partitions and audit records. The fix is not to believe every X-Forwarded-* value. These headers arrive inside an ordinary request and are forgeable unless the application knows which proxy supplied them.

Each forwarded value changes a security decision

ValueASP.NET Core propertyFailure if wrong
X-Forwarded-ForRemote IPFalse audit identity, bypassed IP policy or bad rate limits
X-Forwarded-ProtoRequest schemeRedirect loops, insecure URLs or cookie mistakes
X-Forwarded-HostRequest hostHost-header injection into links and callbacks
Path base/prefixPath baseBroken routing and generated links

Forwarded headers do not “restore reality” automatically. They assert what an upstream component observed. Trust is only as strong as the routing path and proxy configuration that prevents bypass.

Web request travelling through edge proxy to application
Process only the hops whose network identity and header-replacement behaviour are controlled.

Known proxies are the trust boundary

Configure forwarded-header middleware with explicit known proxy addresses or trusted networks. Ensure the application cannot also be reached directly from an untrusted network. If a client can bypass the proxy and connect to Kestrel, it can present the same header names unless network controls block that path.

Cloud proxy addresses can be dynamic, which makes static allowlists awkward. Prefer a private network boundary, service integration or platform-supported forwarding configuration that provides a verifiable last hop. Do not clear known-proxy restrictions globally simply because an address changes.

The proxy should remove or overwrite inbound forwarding headers rather than append blindly to client-supplied values. The application then processes the expected number of trusted hops. Multiple proxies require an explicit chain; more list entries are not more trustworthy.

Forward limits prevent walking into attacker data

Forwarded-for values form a chain as requests traverse proxies. A forward limit tells the middleware how many rightmost trusted hops to consume. Without a correct limit and trust configuration, processing can continue into values supplied before the controlled edge.

Draw the real production path: browser → CDN/edge → regional gateway → ingress → application. Record which component sets which header, whether it overwrites or appends, and which source address the next hop sees. Then configure and test that exact topology. Guessing based on one local proxy is how client IP logs become fiction.

Middleware ordering changes generated URLs

Forwarded headers must run before components that depend on scheme, host or client address: HTTPS redirection, authentication, link generation, rate limiting and application handlers. Otherwise those components make decisions from internal request values and later correction is too late.

A classic symptom is an HTTPS redirect loop. The edge receives HTTPS, forwards HTTP, and the application redirects to HTTPS because it has not yet processed X-Forwarded-Proto. The edge repeats the same journey. Moving or correctly configuring forwarded-header processing fixes the perception; disabling HTTPS protection treats the symptom.

Host deserves validation even after forwarding

X-Forwarded-Host can influence password-reset links, canonical URLs and identity-provider callbacks. Accept only hosts the application is intended to serve. Host filtering at the server or proxy and an application allowlist provide defence in depth.

For security-sensitive externally visible URLs, configuration may be safer than reconstructing origin from a request. A canonical public base URL avoids trusting arbitrary host variations and remains clear when internal and external paths differ. Multi-tenant hostnames require an authoritative tenant-domain mapping, not acceptance of any syntactically valid host.

Client IP is often weaker evidence than teams think

Even correctly forwarded addresses may identify corporate NAT, mobile gateways, privacy relays or another proxy-not a person. Use them for diagnostics, coarse abuse controls and risk signals, not as primary authentication.

IP-based rate limits need a trusted address and bounded partition strategy. Attackers can rotate addresses, while many legitimate users can share one. For authenticated APIs, tenant or client identity is usually a better primary partition with IP as a supporting dimension.

Store only the address detail the operational or legal purpose requires and define retention. Forwarding accuracy does not automatically justify indefinite personal-data collection.

Standard Forwarded and X-Forwarded are not interchangeable by magic

RFC Forwarded and the de facto X-Forwarded-For, -Proto and -Host family encode similar information differently. Your infrastructure and ASP.NET Core configuration must agree on names and syntax. Some platforms use proprietary headers for the original client or path prefix.

Normalise at a controlled proxy where possible instead of teaching every service every vendor convention. Document the normalised contract and test it after platform upgrades; a header-name change can break authentication callbacks without changing application code.

Diagnose from both sides of each hop

Capture a temporary, redacted diagnostic view of socket peer address, raw forwarding headers and the processed scheme, host and remote address. Compare edge logs with application trace identifiers. Do not leave unrestricted header logging enabled; forwarding chains and hosts can contain sensitive or attacker-controlled data.

Test public HTTPS redirects, generated absolute links, secure cookies, authentication callbacks and direct-origin rejection. Include a request with forged forwarding headers through the supported public route and prove the edge overwrites them. Then try to reach the application endpoint directly and confirm the network prevents it.

Reproduce the topology locally

Place a real reverse proxy in front of the application rather than setting headers manually in a browser. Run TLS at the proxy, plain HTTP to Kestrel, and verify processed request properties. Add a second hop if production has one. This catches ordering and chain assumptions that unit tests of configuration objects cannot.

Configuration should be environment-specific but fail safe. Development may trust loopback; production should list controlled hops or use a protected platform boundary. A production fallback of “trust all so redirects work” is not resilience.

Production checklist

  • Draw every proxy hop and header mutation.
  • Prevent direct untrusted access to the application origin.
  • Trust explicit proxy addresses or protected networks.
  • Set the forwarded headers and forward limit intentionally.
  • Run middleware before HTTPS redirection, authentication and routing decisions.
  • Allowlist public hosts or use a configured canonical origin.
  • Test forged inbound headers and origin bypass.
  • Do not treat client IP as identity.
  • Correlate edge and application logs without retaining raw headers unnecessarily.

Related C3 Software guides

Frequently asked questions

Why does HTTPS redirection loop behind a proxy?

The application sees the internal HTTP hop because forwarded scheme processing is missing, untrusted or ordered after redirection.

Can ASP.NET Core trust all forwarded headers?

It can be configured to, but should not be on an untrusted route. Only controlled proxy hops should be able to alter the application's request identity.

Should canonical URLs use X-Forwarded-Host?

Only after proxy and host validation. A configured public origin is safer for security-sensitive absolute URLs when the host is fixed.

Is X-Forwarded-For the user's identity?

No. At best it is a network-origin signal and may represent NAT, relays or shared infrastructure.

Make the proxy boundary explicit

C3 Software Limited has built and supported business software in the UK since 2001. If redirects, callbacks or client-address controls differ behind production infrastructure, talk to C3 Software about a deployment review.