ASP.NET Core无法读取转发头
在我的应用中,我在Program.cs有如下配置
// Fetch Cloudflare IP ranges if not in dev env
IList<string> cloudflareRanges = [];
if (!builder.Environment.IsDevelopment())
{
using SocketsHttpHandler handler = new()
{
PooledConnectionLifetime = TimeSpan.FromMinutes(2)
};
using HttpClient httpClient = new(handler, disposeHandler: false);
string list = await httpClient.GetStringAsync("https://www.cloudflare.com/ips-v4/");
string[] ranges = list.Split("\n");
foreach (string range in ranges)
cloudflareRanges.Add(range);
}
builder.Services.Configure<ForwardedHeadersOptions>(async options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
if (!builder.Environment.IsDevelopment())
{
options.ForwardLimit = 1;
// Trust Cloudflare's IP ranges
foreach (string range in cloudflareRanges)
{
string[] parts = range.Split('/');
options.KnownIPNetworks.Add(new System.Net.IPNetwork(
IPAddress.Parse(parts[0]),
int.Parse(parts[1])));
}
}
});
var app = builder.Build();
ILogger<Program> logger = app.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Running in {0} environment", app.Environment.EnvironmentName);
IList<System.Net.IPNetwork> knownIPNetworks = app.Services.GetRequiredService<IOptions<ForwardedHeadersOptions>>().Value.KnownIPNetworks;
logger.LogInformation(
"The following {0} IP ranges will be trusted: {1}",
knownIPNetworks.Count,
string.Join(", ", knownIPNetworks.Select(n => $"{n.BaseAddress}/{n.PrefixLength}")));
app.UseForwardedHeaders();
app.Use(async (context, next) =>
{
Console.WriteLine($"RemoteIpAddress: {context.Connection.RemoteIpAddress}");
Console.WriteLine($"X-Forwarded-For: {context.Request.Headers["X-Forwarded-For"]}");
Console.WriteLine($"X-Forwarded-Proto: {context.Request.Headers["X-Forwarded-Proto"]}");
Console.WriteLine($"Scheme: {context.Request.Scheme}");
await next();
});
app.UseHttpsRedirection();
app.UseRequestLocalization();
app.UseStatusCodePagesWithReExecute("/status/{0}", createScopeForStatusCodePages: true);
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseAntiforgery();
app.UseMiddleware<UnauthenticatedUserIdentifierMiddleware>();
app.UseMiddleware<ModerationRewritesMiddleware>();
app.UseRouting();
app.UseRateLimiter();
app.MapStaticAssets()
.WithMetadata(new StaticAssetMetadata());
app.MapControllers();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
// Add additional endpoints required by the Identity /Account Razor components.
app.MapAdditionalIdentityEndpoints();
app.Run();
(I know there is a duplicate UseRouting() call but it's intended for internal rewrites where endpoint metadata is needed)
我知道有一个重复的 UseRouting() 调用,但这是为了内部重写,在需要端点元数据时使用
应用运行时,我得到如下输出:
info: Program[0]
Running in Production environment
info: Program[0]
The following 16 IP ranges will be trusted: 127.0.0.0/8, 173.245.48.0/20, 103.21.244.0/22, 103.22.200.0/22, 103.31.4.0/22, 141.101.64.0/18, 108.162.192.0/18, 190.93.240.0/20, 188.114.96.0/20, 197.234.240.0/22, 198.41.128.0/17, 162.158.0.0/15, 104.16.0.0/13, 104.24.0.0/14, 172.64.0.0/13, 131.0.72.0/22
应用在Docker中运行,位于Cloudflare和 NGINX之后,我有如下配置文件
server {
listen 80;
server_name example.com;
# Redirect all HTTP traffic to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
# SSL Configuration
ssl_certificate /etc/ssl/certs/app-cloudflare.pem;
ssl_certificate_key /etc/ssl/private/app-cloudflare.key;
access_log /var/log/nginx/app_headers.log debug_proxy_headers;
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Map block to handle WebSocket connection upgrades dynamically
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
app_headers.log显示如下:
"GET / HTTP/1.1" 200 316 Host: "example.com" X-Forwarded-For: "MY_IP_ADDRESS, 162.158.116.84" X-Forwarded-Proto: "https" Scheme: "https"
然而在 app.UseForwardedHeaders() 之后执行的代码根本看不到转发头,如下输出所示:
2026-05-17T13:17:32.592944997Z RemoteIpAddress: ::ffff:172.18.0.1
2026-05-17T13:17:32.592972238Z X-Forwarded-For: MY_IP_ADDRESS, 162.158.116.145
2026-05-17T13:17:32.592975118Z X-Forwarded-Proto: https
2026-05-17T13:17:32.592988278Z Scheme: http
流程是这样的 浏览器 > Cloudflare > NGINX > Docker > 我的应用。
从最后的输出可以看到,ASP.NET Core没有拾取转发头,RemoteIpAddress显示为Docker的网关IP而不是我的IP,Scheme是 http而不是转发后的https。
我不认为是信任问题,因为在 X-Forwarded-For 中的代理IP是 162.158.116.145,属于 162.158.0.0/15 范围。
解决方案
问题在于Docker网关IP未被信任,参见 文档
The request's original remote IP must match an entry in the KnownProxies or KnownNetworks lists before forwarded headers are processed. This limits header spoofing by not accepting forwarders from untrusted proxies