﻿# Configuring redirect headers in .NET when working with Azure Application Gateway

- Canonical URL: https://taraskovalenko.github.io/en/posts/agw-hostname/
- Published: 2025-04-25
- Categories: .net, C#, azure, Application Gateway
- Tags: .net, C#, OpenID, Azure, Application Gateway, Middleware

When we deploy a .NET application on `Azure App Service` and configure access to it via `Azure Application Gateway`, there is a typical problem of determining the correct host and request scheme.
The crux of the problem is that the request goes through several layers to your application:

- The user sends a request to `https://myapp.example.com`
- The request goes to `Application Gateway`
- `Application Gateway` redirects the request to `App Service`
- `App Service` forwards the request to your application

At each stage, information about the original request (user IP address, scheme, host) may be lost or replaced with internal information. As a result, when the request reaches your application, `HttpContext` does not contain the same data that was in the original request.
For example, if your user logs in via `https://myapp.example.com`, the request might arrive at your application as `http://internal-appservice-ip:port`. This leads to the following problems:

- Incorrect URL generation - all links generated by your application will contain the wrong host and scheme
- Authentication errors - especially with `OpenIddict` or other `OpenID Connect` implementations that strictly check redirect URLs
- Problems with `CORS` - incorrect definition of the Origin header
- Bugs `middleware` - many .NET components depend on correct schema and host definition

## Solution

.NET has a built-in mechanism for handling redirected headers that can be configured to work correctly with proxy servers.

Below is the code that solves the problem:

```csharp
services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = 
       ForwardedHeaders.XForwardedFor | 
       ForwardedHeaders.XForwardedProto |
       ForwardedHeaders.XForwardedHost;
    
    options.ForwardedHostHeaderName = "X-Original-Host";
    
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});
```

Let's analyze what happens in this code:

### Setting ForwardedHeaders

```csharp
options.ForwardedHeaders = 
   ForwardedHeaders.XForwardedFor | 
   ForwardedHeaders.XForwardedProto |
   ForwardedHeaders.XForwardedHost;
```

Here we specify exactly which redirect headers should be processed:

- `XForwardedFor` - defines the original IP address of the client
- `XForwardedProto` - defines the original protocol (http or https)
- `XForwardedHost` - specifies the original host specified in the request

### Optional host header configuration

```csharp
options.ForwardedHostHeaderName = "X-Original-Host";
```

This line is critical for our scenario with `Azure Application Gateway`. It tells .NET to look at the non-standard `X-Original-Host` header to determine the originating host.
Often `Azure Application Gateway` or other proxies are configured to transmit the original host in this header instead of the standard `X-Forwarded-Host`.

### Clearing lists of trusted networks and proxies

```csharp
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
```

By default, .NET accepts redirect headers only from trusted proxies for security. By clearing these lists, we allow headers to be accepted from any source.

## Example problem with OpenIddict

Consider a specific example of a problem with `OpenIddict`:

Let's say your app is deployed at `Application Gateway` and users access it at `https://myapp.example.com`.
However, the application itself sees requests coming from `Application Gateway` from the local network as `http://internal-ip:port`, for example.
When setting `OpenIddict`, you specify the redirect URL as `https://myapp.example.com/signin-callback`. When a user authenticates, `OpenIddict` tries to check if the redirect URL matches the configured one. But because the app "thinks" it's running at `http://internal-ip:port`, it generates a redirect URL as `http://internal-ip:port/signin-callback`.

Result: URL incompatibility results in an authentication error with a message like `"Invalid redirect_uri"`.

## Applying the solution in Startup.cs

To completely solve the problem, in addition to setting options, you must also add middleware to handle redirected headers in the Configure method:

```csharp
 // It is important to add this at the very beginning of PayPal's request processing
app.UseForwardedHeaders();
```

## Customization features for Azure App Service

Additionally, it's worth noting that when your app is deployed on `Azure App Service` by `Application Gateway`, the header redirection problem becomes particularly acute. `App Service` has its own load balancing infrastructure that adds another layer of proxying:

`User → Application Gateway → App Service infrastructure → Your application`

Each layer can modify the headers, so the correct configuration of `ForwardedHeadersOptions` is critical to ensure that the application works correctly in such an architecture.
Also, when deploying to `App Service`, it is recommended to check the settings of `ssl termination`. If SSL termination occurs at `Application Gateway`, then requests to your application may arrive at `HTTP` even if the client is using HTTPS.
In this case, correct processing of `XForwardedProto` will ensure the correct definition of the scheme.

## Conclusion

Correctly configuring redirect headers is critical when deploying .NET applications on `Azure App Service` behind proxies, especially if you use authentication protocols such as `OpenID Connect`.
This solution allows the application to correctly determine the original address of the request, which ensures the correct operation of URL generation, authentication and other components that depend on the exact definition of the host and the request scheme.

{: .prompt-info }
Don't forget to configure a list of trusted proxies in a real scenario to increase the security of your application.
