Proxies are vital for load balancing and security, but they obscure the actual client IP, scheme, and domain, causing broken links, inaccurate logging, and other headaches. In this post, we’ll look at how ASP.NET Core’s Forwarded Headers Middleware restores these details so your services behave as though they’re directly on the public internet.
What is the Forwarded Headers Middleware?
In ASP.NET Core, requests travel through a pipeline made of various middleware components. The Forwarded Headers Middleware is one such component that often comes pre-configured (or can be easily enabled) to handle forwarded headers from proxies. It’s typically placed among the first modules in the pipeline, as shown below:
What problem does the Forwarded Headers Middleware solve?
In many scenarios, our services and applications are deployed behind a proxy server, which may serve various roles, such as:
- Load balancer
- Web Application Firewall (WAF)
- Reverse proxy
- Content cache
- TLS Termination
- …
The example below shows three service instances behind a proxy, acting as a load balancer.
The general problem is that the details about the original request are lost as it passes through the proxy.
In other scenarios, a single proxy might manage traffic for multiple domain names, and the services need to identify the original request to return the correct content to the caller.
Making ASP.NET Core Services Proxy-Aware
The goal is to make the internal services act as though they are directly accessible on the public internet, even though they are behind a proxy.
To solve this, we somehow need to:
- Recognize the public URL that the user accessed instead of the internal network address.
- Identify the original client IP address rather than the IP address of the proxy.
- Determine the scheme of the original request (HTTP or HTTPS).
As shown below, we want the application in service1 to believe it received a request to https://tn-data.se rather than http://service1.
Let’s get a bit more technical
Let’s dive a bit deeper into the technical details. Imagine a setup with a proxy sitting between the client and the service:
How can we make Service1 behave as if it received a request from IP 193.8.1.12 to https://tn-data.se instead of IP 10.0.0.1 to http://service1?
Introducing the X-Forwarded Headers
The first step is configuring the proxy to add a set of headers to the outgoing request describing the original request received by the proxy.
The X-Forwarded-* headers are special HTTP headers that help servers understand where a request came from when it passes through intermediaries like proxies or load balancers. They provide essential details such as the original client’s IP address, the protocol used, and the host requested.
Proxies can add these headers to the request, informing the service about the originating IP, scheme, and host.
Let’s explore how we handle this in ASP.NET Core next.
ASP.NET Core and the X-Forwarded Headers
The Forwarded Headers Middleware is responsible for rewriting the request when these headers are present in the incoming request.
Using the details from the above example, here’s what happens to the request as it passes through the middleware:
Steps taken:
- The middleware adds X-Original-* headers to the request, containing details about the current request.
- It then rewrites the request based on the information found in the X-Forwarded-* headers.
Forwarded Headers in Azure Container Apps
To observe these headers in action, we can deploy my open-source Cloud Debugger tool as an Azure Container App. This tool allows us to inspect the request before and after this middleware has processed it.
For example, in the image above:
- The request scheme has been updated from HTTP to HTTPS.
- The sender’s IP address has changed from ::ffff:100.100.0.60 (a local IPv6 Azure address) to 193.150.241.131 (my computer’s public IP address).
- The ::ffff:x.x.x.x format is an IPv6 representation of an IPv4 address, mapping it into the IPv6 space.
This shows how the middleware reconstructs the original request context using the X-Forwarded-* headers.
In the cloud debugger for Azure, we also get this nice overview of the headers:
Here, two X-Forwarded-* headers are received, and two corresponding X-Original-* headers are created with the updated information.
What About Security?
The Forwarded Headers Middleware offers several ways to validate and control how X-Forwarded-* headers are processed. Because blindly trusting them can be risky. Attackers or misconfigured proxies might send bogus headers, leading to incorrect IP addresses, spoofed schemes, and other vulnerabilities. In a future post, we’ll explore best practices to secure and configure these headers properly, including limiting trusted networks and carefully validating header values.
Summary
In this blog post, I examined the purpose and functionality of the Forwarded Headers Middleware in ASP.NET Core. This middleware processes X-Forwarded-* headers to restore information about the original request, including the client’s IP address, the request scheme (HTTP or HTTPS), and the public URL. The middleware modifies the request with these details while preserving the original values in dedicated headers for reference.
In a future post, I’ll provide a hands-on guide to configuring and working with these headers in your applications.
About the Author
Hi, I’m Tore! I have been fascinated by computers since I unpacked my first Commodore VIC-20. Today, I enjoy providing freelance development and developer training services, focusing on ASP.NET Core, IdentityServer, OpenID Connect, Architecture, and Web Security. You can connect with me on LinkedIn and BlueSky, and you can find out more about me and my services here, as well as my courses for developers. Tore is also a Microsoft .NET MVP.