azure-functions-host
azure-functions-host copied to clipboard
X-Forwarded-Host header missing in dotnet 8 isolated
The X-Forwarded-Host header provided by Azure Front Door is missing in a fresh new function in dotnet 8 isolated. Is working fine in dotnet 6 in-process but stops working when migrating to 8 isolated.
Investigative information
Please provide the following:
- Timestamp:
- Function App version: 4
- Function App name:
- Function name(s) (as appropriate):
- Invocation ID:
- Region: France Central
Repro steps
- create an Azure Function in dotnet 8 isolated
- in a function
testforwardheader, output the value of the header X-Forwarded-Hostreq.Headers.TryGetValue("X-Forwarded-Host", out StringValues value) - publish the function behind a Front Door with redirect rules to this app from the url
subdomain.myfrontdoordomain.com/myfunction. - call the function using
subdomain.myfrontdoordomain.com/myfunction/api/testforwardheader
Expected behavior
in step 4 you should get the host requested by client, so subdomain.myfrontdoordomain.com
Actual behavior
in step 4 you get an empty string
Known workarounds
Someone found a workaround. They were able to find and restore the value from the bindingContext.BindingData using custom middleware.
Workaround
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Middleware;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
var builder = new HostBuilder();
var host = builder
.ConfigureFunctionsWebApplication(
app =>
{
app.UseMiddleware<ForwardedForHeaderMiddleware>();
})
.Build();
await host.RunAsync();
public class ForwardedForHeaderMiddleware : IFunctionsWorkerMiddleware
{
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
await CorrectForwardedForAsync(context);
await next(context);
}
private static async Task CorrectForwardedForAsync(FunctionContext context)
{
var val = GetForwardedFor(context);
var req = await context.GetHttpRequestDataAsync();
req?.Headers.TryAddWithoutValidation(ForwardedHeadersDefaults.XForwardedForHeaderName, val);
}
private static string? GetForwardedFor(FunctionContext context)
{
var hdr = context.BindingContext.BindingData["Headers"];
dynamic obj = JsonConvert.DeserializeObject(hdr.ToString());
var val = obj[ForwardedHeadersDefaults.XForwardedForHeaderName]?.Value as string;
return val;
}
}
Related information
written in C#
I know that the Front Door will send X-Forwarded-Host and X-Forwarded-Proto which we were able to use with the older Swashbuckle library which we used for In-Process Function Apps.
@paulverbeke are you using the ASP.NET Core integration for your tests? In the HTTP proxying mode, all headers should be forwarded and will be processed by the worker.
@fabiocav thanks for getting back quickly. Yes we are using ASP.NET Core integration. But I couldn't find anything online regarding "HTTP proxying mode". Is there something I need to enable or specify somewhere ?
EDIT: I just tested the starting Azure Functions template without ASP.NET Core integration (with HttpRequestData/HttpResponseData) and the header is available. Is it expected behavior that ASP.NET Core integration is causing header to be dropped ? If so why the default Visual Studio template is showing an example function with ASP.NET Core integration without this drawback be documented somewhere ?
@fabiocav are there any updates on this? I'm also experiencing the same issue
I'm also encountering this issue with my migration from .net 6 in process to .net isolated.
I'm encountering the same issue. It looks like it does not matter who is providing the X-Forwarded-For header. Also the X-Forwarded-Host header is also affected.
That's unexpected and undocumented, breaking change.
@fabiocav Any news on this? It's unclear from this issue if this is a bug or a undocumented change. Any update appreciated.
It seems that the X-Forwarded-* headers are being removed because ForwardedHeadersMiddleware is executed inside .ConfigureFunctionsWebApplication().
.ConfigureFunctionsWebApplication()
If this behavior is not desired, it might be possible to work around it by doing the following:
var builder = new HostBuilder();
builder
.ConfigureFunctionsWebApplication(static builder =>
{
// add these lines
builder.Services.PostConfigure<ForwardedHeadersOptions>(static o =>
{
o.ForwardLimit = 0;
o.ForwardedHeaders = ForwardedHeaders.None;
});
});
var host = builder.Build();
host.Run();
However, please be aware that disabling the processing of X-Forwarded-* may have side effects, so it’s best to use this approach with careful consideration.
However, please be aware that disabling the processing of
X-Forwarded-*may have side effects, so it’s best to use this approach with careful consideration.
I tried this workaround for an Azure Function as the X-Forwarded headers are missing sometimes for me (not always - just for some requests!). Anyhow, unfortunately, this had a side-effect of breaking my Easy Auth login routines because it impacted the request Url (changing it to a localhost address instead of the custom domain).
// Redirect the user to the Easy Auth login url if they're not authenticated
var baseUrl = $"{req.Url.Scheme}://{req.Url.Authority}";
var loginUrl = $"{baseUrl}/.auth/login/aad?post_login_redirect_url={Uri.EscapeDataString(returnUrl)}";
...
Just wanted to warn anyone else of the possible side-effects.