How to Fix Missing X-Forwarded-Host Header in IIS Reverse Proxy Setup


4 views

When configuring IIS (Internet Information Services) on Windows Server 2016 as a reverse proxy for Kestrel web servers, many developers encounter an issue where the X-Forwarded-Host header isn't being forwarded to the backend servers, while other X-Forwarded-* headers like X-Forwarded-For and Forwarded-Proto work correctly.

By default, IIS's Application Request Routing (ARR) module doesn't automatically forward the X-Forwarded-Host header. This is different from other reverse proxies like Nginx or Apache, which typically forward all X-Forwarded-* headers by default.

To fix this, you'll need to manually configure IIS to forward the X-Forwarded-Host header. Here's how:

Method 1: Using URL Rewrite Outbound Rules

Add this rule to your web.config:

<rewrite>
  <outboundRules>
    <rule name="Add X-Forwarded-Host">
      <match serverVariable="HTTP_X_Forwarded_Host" pattern=".*" />
      <action type="Rewrite" value="{HTTP_HOST}" />
    </rule>
  </outboundRules>
</rewrite>

Method 2: Using Server Variables

Alternatively, you can enable the server variable:

  1. Open IIS Manager
  2. Select your server in the connections pane
  3. Open "Application Request Routing Cache"
  4. Click "Server Proxy Settings"
  5. Under "Allowed Server Variables", add "HTTP_X_FORWARDED_HOST"

After implementing either solution, test with a request to verify the header is being forwarded:

curl -v http://your-iis-server.com

You should now see the X-Forwarded-Host header in your Kestrel application's request headers.

If you're using ASP.NET Core, you might want to configure the Forwarded Headers middleware:

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | 
                      ForwardedHeaders.XForwardedHost | 
                      ForwardedHeaders.XForwardedProto
});

Remember that proper header forwarding is crucial for applications that need to:

  • Generate correct absolute URLs
  • Implement proper redirects
  • Maintain security headers
  • Handle authentication properly

When setting up IIS as a reverse proxy for Kestrel servers, many developers notice that while X-Forwarded-For and X-Forwarded-Proto headers get forwarded automatically, X-Forwarded-Host mysteriously disappears. This isn't a configuration oversight - IIS's Application Request Routing (ARR) module handles this differently than other forwarded headers.

The X-Forwarded-Host header is crucial when your application needs to:

  • Generate absolute URLs with the correct hostname
  • Handle CORS policies correctly
  • Maintain consistent session cookies across domains
  • Implement proper redirects in multi-tenant applications

Unlike other X-Forwarded-* headers, IIS doesn't automatically forward X-Forwarded-Host. Here's how to properly configure it:

<system.webServer>
    <rewrite>
        <allowedServerVariables>
            <add name="HTTP_X_FORWARDED_HOST" />
        </allowedServerVariables>
        <rules>
            <rule name="ForwardedHostHeader">
                <match url="(.*)" />
                <serverVariables>
                    <set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
                </serverVariables>
                <action type="None" />
            </rule>
        </rules>
    </rewrite>
</system.webServer>

After applying these changes, verify the headers are being forwarded correctly. In your Kestrel application, add this middleware to inspect headers:

app.Use(async (context, next) =>
{
    var headers = context.Request.Headers
        .Where(h => h.Key.StartsWith("X-Forwarded"))
        .ToList();
    
    // Log or debug these headers
    await next.Invoke();
});

If you have multiple proxy layers, you'll need to append rather than overwrite the X-Forwarded-Host header:

<set name="HTTP_X_FORWARDED_HOST" value="{HTTP_X_FORWARDED_HOST}, {HTTP_HOST}" />

Remember to configure your Kestrel application to properly handle forwarded headers:

services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.All;
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});
  • Ensure the ARR module is installed and enabled
  • Double-check server variable permissions
  • Verify the rewrite module is processing the rules
  • Check for conflicting rules in web.config hierarchy