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 Gatewayredirects the request toApp ServiceApp Serviceforwards 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
OpenIddictor otherOpenID Connectimplementations 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:
1
2
3
4
5
6
7
8
9
10
11
12
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
1
2
3
4
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 clientXForwardedProto- defines the original protocol (http or https)XForwardedHost- specifies the original host specified in the request
Optional host header configuration
1
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
1
2
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:
1
2
// 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.
Don’t forget to configure a list of trusted proxies in a real scenario to increase the security of your application.