ASP.NET - ASP.NET Core Forwarded Headers and Reverse Proxy Integration

When an ASP.NET Core application is deployed behind a reverse proxy or load balancer, the application does not always receive the original details of the client's request directly. Instead, the proxy receives the request first and then forwards it to the ASP.NET Core application. This can cause the application to see the proxy's IP address, HTTP scheme, or internal host rather than the information associated with the original client request.

ASP.NET Core provides Forwarded Headers Middleware to solve this problem. It reads specific HTTP headers added by trusted proxies and uses them to restore information about the original request. Microsoft specifically recommends configuring this middleware when ASP.NET Core is running behind proxies or load balancers.

1. What Is a Reverse Proxy?

A reverse proxy is a server that sits between users and an application server.

A typical architecture looks like this:

Client
   |
   | HTTPS request
   v
Reverse Proxy
   |
   | HTTP/HTTPS request
   v
ASP.NET Core Application

The client may access:

https://www.example.com

However, the reverse proxy may communicate with the ASP.NET Core application through:

http://10.0.0.20:5000

From the application's perspective, the immediate connection is coming from the reverse proxy rather than directly from the client.

Common reverse proxies and infrastructure components include:

  • Nginx

  • Apache

  • IIS

  • Cloud load balancers

  • Application gateways

  • Kubernetes ingress controllers

  • Other network proxies

Reverse proxies are commonly used for SSL/TLS termination, load balancing, routing, security, and exposing applications through a public domain.

2. Why Does ASP.NET Core Need Forwarded Headers?

Consider a user accessing an application using HTTPS:

Client
https://example.com
        |
        v
Reverse Proxy
        |
        v
ASP.NET Core

The reverse proxy may terminate the HTTPS connection and communicate with the ASP.NET Core application over HTTP.

The application could therefore see:

Request.Scheme = "http"

even though the user originally accessed:

https://example.com

This difference can cause problems with HTTPS redirection, authentication, URL generation, cookies, and external authentication providers. Microsoft notes that incorrect forwarded-header configuration can even result in redirect loops when HTTPS redirection is used. 

The same problem can occur with the client's IP address.

Instead of seeing:

203.0.113.25

the ASP.NET Core application may see the reverse proxy's address:

10.0.0.100

Forwarded headers allow the proxy to communicate the original request information to the application.

3. Important Forwarded Headers

Several headers are commonly used.

X-Forwarded-For

X-Forwarded-For contains information about the original client IP address and potentially the addresses of proxies through which the request has passed.

For example:

X-Forwarded-For: 203.0.113.25

ASP.NET Core can use this information to populate:

HttpContext.Connection.RemoteIpAddress

This is particularly useful when applications need the client's IP address for logging, auditing, rate-limiting, or other request-processing purposes. 

X-Forwarded-Proto

X-Forwarded-Proto identifies the original request scheme.

For example:

X-Forwarded-Proto: https

ASP.NET Core can use this information to set:

HttpContext.Request.Scheme

This allows the application to understand that the original request was HTTPS even if the proxy communicated with the application using HTTP.

X-Forwarded-Host

X-Forwarded-Host contains the original host requested by the client.

For example:

X-Forwarded-Host: www.example.com

ASP.NET Core can use this information to populate the request host.

X-Forwarded-Prefix

X-Forwarded-Prefix can be used when an application is exposed under a path prefix through a proxy.

For example:

https://example.com/myapp

The proxy can communicate the /myapp prefix to the application through this header.

ASP.NET Core's forwarded-header middleware supports these forwarded values and maps them to the corresponding properties of HttpContext

4. Forwarded Headers Middleware

ASP.NET Core provides the UseForwardedHeaders() middleware for processing these headers.

A basic configuration looks like this:

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor |
        ForwardedHeaders.XForwardedProto;
});

var app = builder.Build();

app.UseForwardedHeaders();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

The important part is:

options.ForwardedHeaders =
    ForwardedHeaders.XForwardedFor |
    ForwardedHeaders.XForwardedProto;

This tells ASP.NET Core which forwarded headers should be processed.

Without configuring the desired forwarded headers, the default ForwardedHeaders value is None

5. Middleware Ordering

The position of UseForwardedHeaders() in the middleware pipeline is important.

For example:

app.UseForwardedHeaders();

app.UseHttpsRedirection();

app.UseAuthentication();

app.UseAuthorization();

app.MapControllers();

Forwarded headers should generally be processed before middleware that depends on the original request information.

For example, HTTPS redirection needs to know whether the original request was HTTP or HTTPS. If the forwarded scheme has not been processed first, the application can incorrectly believe that an HTTPS request was HTTP.

Microsoft recommends placing forwarded-header processing before UseHsts() and other middleware that relies on the forwarded request information. 

6. Reverse Proxy Example

Suppose a user sends:

GET https://example.com/products

The request reaches a reverse proxy.

The proxy forwards something similar to:

GET http://10.0.0.20:5000/products

X-Forwarded-For: 203.0.113.25
X-Forwarded-Proto: https
X-Forwarded-Host: example.com

Without forwarded-header processing, ASP.NET Core might see:

RemoteIpAddress = 10.0.0.100
Scheme = http
Host = 10.0.0.20:5000

After forwarded headers are correctly processed, the application can see values corresponding to the original request:

RemoteIpAddress = 203.0.113.25
Scheme = https
Host = example.com

This makes the application behave as though it understands the public-facing request even though a proxy sits between the client and application.

7. Security Considerations

Forwarded headers should not automatically be trusted from arbitrary clients.

This is one of the most important aspects of reverse-proxy configuration.

An attacker could potentially send a request containing a forged header such as:

X-Forwarded-For: 1.2.3.4

If the application blindly trusts this value, it could incorrectly treat the attacker as coming from another IP address.

ASP.NET Core therefore provides mechanisms such as:

KnownProxies

and:

KnownNetworks

These allow applications to specify which proxies or networks are trusted to provide forwarded-header information. Microsoft specifically warns that only trusted proxies and networks should be permitted to forward these headers because otherwise IP spoofing can occur. 

For example:

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor |
        ForwardedHeaders.XForwardedProto;

    options.KnownProxies.Add(
        IPAddress.Parse("10.0.0.100"));
});

Here, 10.0.0.100 is explicitly identified as a trusted proxy.

8. ForwardLimit

Applications can also control how many forwarded values are processed.

For example:

options.ForwardLimit = 2;

This can be useful when requests pass through multiple proxy servers.

A request might travel like this:

Client
   |
Proxy 1
   |
Proxy 2
   |
ASP.NET Core

The forwarded headers could contain information from multiple hops.

ASP.NET Core's default ForwardLimit is 1. When multiple proxy hops are expected, the configuration needs to reflect the actual trusted infrastructure. 

9. Forwarded Headers Behind Nginx

A common deployment scenario is:

Internet
   |
   v
Nginx
   |
   v
ASP.NET Core / Kestrel

Nginx acts as the public-facing reverse proxy while Kestrel runs the ASP.NET Core application.

The proxy should forward the necessary information, while ASP.NET Core should be configured to process it.

For example, the application can configure:

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor |
        ForwardedHeaders.XForwardedProto;
});

var app = builder.Build();

app.UseForwardedHeaders();

This allows ASP.NET Core to correctly interpret information about the original connection.

10. Problems Caused by Incorrect Configuration

Incorrect forwarded-header configuration can produce several unexpected behaviors.

Incorrect client IP

Application logs may record the proxy's IP address instead of the client's address.

HTTPS redirect loops

The proxy receives HTTPS but communicates with the application through HTTP. If ASP.NET Core doesn't know that the original request was HTTPS, HTTPS redirection can repeatedly redirect the request.

Incorrect generated URLs

Applications that generate absolute URLs may produce:

http://example.com

instead of:

https://example.com

Authentication problems

OAuth and OpenID Connect authentication flows rely heavily on correct redirect URLs. Incorrect scheme or host information can cause authentication callbacks to fail. Microsoft specifically identifies incorrect redirects as a consequence of improper proxy configuration. 

Incorrect host information

If the application uses the internal host supplied by the proxy instead of the public host, generated links and redirects may point to an internal server address.

11. Forwarded Headers and HTTPS

One particularly important use case is HTTPS.

Consider:

Browser
   |
HTTPS
   |
Reverse Proxy
   |
HTTP
   |
ASP.NET Core

The browser is using HTTPS, but the ASP.NET Core server receives HTTP.

The proxy can send:

X-Forwarded-Proto: https

ASP.NET Core then processes this information and updates:

HttpContext.Request.Scheme

to reflect HTTPS.

This is why forwarded headers should be processed before HTTPS-related middleware. Microsoft specifically recommends this ordering for applications behind reverse proxies. 

12. Difference Between Proxy and Reverse Proxy

A forward proxy generally acts on behalf of the client:

Client
   |
Forward Proxy
   |
Internet

A reverse proxy acts on behalf of the server or application:

Internet
   |
Reverse Proxy
   |
Application

ASP.NET Core applications commonly encounter reverse proxies because production systems frequently place a proxy or load balancer in front of application servers.

13. Real-World Architecture

A larger production architecture could look like:

                    Internet
                       |
                       v
                Load Balancer
                       |
              +--------+--------+
              |                 |
              v                 v
          Proxy Server      Proxy Server
              |                 |
              v                 v
        ASP.NET Core       ASP.NET Core
          Server 1           Server 2

In this situation, the application needs to correctly understand forwarded request information regardless of which server receives the request.

This becomes especially important when applications are deployed in cloud environments, containers, Kubernetes clusters, or web farms.

14. Best Practices

When using ASP.NET Core behind a reverse proxy, follow these principles:

  1. Identify exactly which proxy or load balancer is in front of the application.

  2. Determine which forwarded headers the proxy generates.

  3. Configure ForwardedHeadersOptions accordingly.

  4. Call UseForwardedHeaders() early in the middleware pipeline.

  5. Configure KnownProxies or KnownNetworks where appropriate.

  6. Do not blindly trust forwarded headers from untrusted clients.

  7. Verify HTTPS behavior after deployment.

  8. Test authentication redirects when using OAuth or OpenID Connect.

  9. Test generated absolute URLs.

  10. Verify that application logs record the intended client IP.

15. Summary

ASP.NET Core Forwarded Headers and Reverse Proxy Integration is the process of making an ASP.NET Core application correctly understand information about the original client request when the application is located behind a reverse proxy or load balancer.

The most important headers are:

X-Forwarded-For
X-Forwarded-Proto
X-Forwarded-Host
X-Forwarded-Prefix

ASP.NET Core's forwarded-header middleware reads these values and updates the corresponding request information, such as the remote IP address, scheme, host, and path base. 

The key principle is that forwarded headers improve the application's understanding of the original request, but they must only be trusted when they originate from trusted proxy infrastructure. Proper configuration prevents problems with client IP detection, HTTPS redirects, authentication, URL generation, and other proxy-dependent functionality.