Modifying host headers with Azure websites when using it behind an Application Gateway or reverse proxy via URL Rewrite Module
I'm currently using Azure's Application Gateway with a backend pool utilising Azure's App Service. When the application gateway forwards your request to the backpool, it also forwards X-Original-Host
HTTP Header together with it to help you identity which listener the gateway originally acted upon but what if you wanted it to match the HOST
header at the web server (app service)? This might be useful for cases where you need to generate absolute URLs in your web app for whatever reason be it sitemaps or some syndication feed etc. Since we are using Azure's App service, we don't have as much control over IIS as we are used to but luckily for us Azure has provided us with a way to transform the applicationHost.config
via transforms.
On the Azure Portal head over to your app service then jump over to the Kudu > Debug console > CMD
and create a file name applicationHost.xdt
under d:\home\site
directory which allows you set the allowed server variables for IIS by overriding/transforming the applicationHost.config file
applicationHost.xdt
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.webServer>
<rewrite>
<allowedServerVariables xdt:Transform="Replace">
<add name="HTTP_X_ORIGINAL_HOST" xdt:Transform="InsertIfMissing" />
<add name="HTTP_HOST" xdt:Transform="InsertIfMissing" />
</allowedServerVariables>
</rewrite>
</system.webServer>
</configuration>
Then on Web.config
file within d:\home\site\wwwroot
you'll need a new rule which basically sets the HTTP_HOST
server variable to be {HTTP_X_ORIGINAL_HOST}
(surrounding curly brace means to evaluate) on the condition that the value of the http header X-Original-Host
is NOT empty (e.g. ^$
).
X-Original-Host
is a HTTP header which Azure's Application Gateway forwards to its backend pools
Web.config
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Set HOST header based on X-Original-Host header">
<match url="(.*)"></match>
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_X_ORIGINAL_HOST}" pattern="^$" negate="true" />
</conditions>
<serverVariables>
<set name="HTTP_HOST" value="{HTTP_X_ORIGINAL_HOST}"></set>
</serverVariables>
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
IMPORTANT : Once the two changes are made, we will need to restart BOTH the SCM site (via the Restart Site
button on {foo}.scm.azurewebsites.net/SiteExtensions) and also the website itself (Via the Azure Portal, CLI or Powershell) or the changes won't kick in.
And to test whether the correct headers are being transformed, simply add some debugging code in your html (or razor page in my case of ASP.NET MVC)
Index.cshtml
<div class="jumbotron">
<p>HTTP_HOST : @Request.ServerVariables["HTTP_HOST"]</p>
<p>HTTP_X_ORIGINAL_HOST : @Request.ServerVariables["HTTP_X_ORIGINAL_HOST"]</p>
</div>
<div class="jumbotron">
<p>X-Forwarded-For : @Request.Headers["X-Forwarded-For"]</p>
<p>X-Forwarded-Proto : @Request.Headers["X-Forwarded-Proto"]</p>
<p>X-Original-Host : @Request.Headers["X-Original-Host"]</p>
</div>
Side note for deployments outside of App Service
If we have greater control over the hosting environment, perhaps using App Service for Windows Containers (in Public preview at time of writing) the same could be achieved using powershell with Set-WebConfigurationProperty
cmdlet in your Dockerfile
while building your image. The below example assumes you've built your image using the Default Web Site
but can obviously be changed to however your website is configured.
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -location 'Default Web Site' `
-filter 'system.webServer/rewrite/allowedServerVariables' -Name '.' `
-value ( @{ name = 'HTTP_HOST' }, @{ name = 'HTTP_X_ORIGINAL_HOST' } )
Which would produce
applicationHost.config
<configuration>
<location path="Default Web Site" overrideMode="Allow">
<system.webServer>
<rewrite>
<allowedServerVariables>
<add name="HTTP_X_ORIGINAL_HOST" />
<add name="HTTP_HOST"/>
</allowedServerVariables>
</rewrite>
<asp />
</system.webServer>
</location>
</configuration>
Here is the Dockerfile
for completeness
Dockerfile
ARG LTSC_VERSION=ltsc2016
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.7.2-windowsservercore-${LTSC_VERSION}
RUN iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
RUN choco install urlrewrite -y
WORKDIR /inetpub/wwwroot
RUN Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -location 'Default Web Site' -filter 'system.webServer/rewrite/allowedServerVariables' -Name '.' -value ( @{ name = 'HTTP_HOST' }, @{ name = 'HTTP_X_ORIGINAL_HOST' } )
ARG PUBLISH_DIR
ADD output/${PUBLISH_DIR}/. ./