You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core无法识别Forwarded Headers问题求助

问题描述

ASP.NET Core 应用部署链路为:浏览器 > Cloudflare > NGINX > Docker > 应用。已配置Forwarded Headers但未生效,具体表现:

  • RemoteIpAddress 显示Docker网关IP(::ffff:172.18.0.1)而非真实用户IP
  • Request.Scheme 显示http而非转发的https
  • 已确认Cloudflare的IP段(如162.158.0.0/15)已加入信任列表,且X-Forwarded-For中的代理IP属于该信任段

相关配置与日志

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.Run();

(注:重复的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

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;
}

NGINX 访问日志(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"

应用输出日志

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

解决方案

1. 修复ForwardedHeadersOptions的异步配置问题

Configure<ForwardedHeadersOptions>不支持异步委托,会导致Cloudflare IP段未正确加载到信任列表中。修改为同步获取IP段:

// 替换原异步获取IP段逻辑
if (!builder.Environment.IsDevelopment())
{
    using var handler = new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(2) };
    using var httpClient = new HttpClient(handler, disposeHandler: false);
    
    // 同步获取Cloudflare IP段
    string list = httpClient.GetStringAsync("https://www.cloudflare.com/ips-v4/").GetAwaiter().GetResult();
    // 移除空行和换行符
    string[] ranges = list.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
    
    foreach (string range in ranges)
        if (!string.IsNullOrWhiteSpace(range))
            cloudflareRanges.Add(range);
}

// 配置ForwardedHeadersOptions使用同步委托
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;

    if (!builder.Environment.IsDevelopment())
    {
        options.ForwardLimit = 1;
        options.KnownProxies.Clear(); // 清空默认信任的代理
        
        // 添加Cloudflare IP段到信任列表
        foreach (string range in cloudflareRanges)
        {
            string[] parts = range.Split('/');
            options.KnownIPNetworks.Add(new System.Net.IPNetwork(
                IPAddress.Parse(parts[0]),
                int.Parse(parts[1])));
        }
        // 额外信任直接转发请求的Docker网关IP
        options.KnownProxies.Add(IPAddress.Parse("172.18.0.1"));
    }
});

2. 调整中间件顺序

确保UseForwardedHeaders是第一个被调用的中间件,在所有请求处理中间件之前:

var app = builder.Build();
// 日志输出部分保留

// 必须放在最前面
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();
// 其余中间件注册保留

3. 优化NGINX转发配置

修改X-Forwarded-For配置,避免NGINX添加自身IP到转发头中,符合ForwardLimit=1的信任逻辑:

# 替换原X-Forwarded-For配置
proxy_set_header X-Forwarded-For $http_x_forwarded_for;

4. 启用调试日志排查

添加日志过滤,查看ForwardedHeaders中间件的处理细节:

builder.Logging.AddFilter("Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersMiddleware", LogLevel.Debug);

启动后检查日志,确认是否有“Trusted proxy detected”相关调试信息,验证信任IP是否被正确识别。


内容的提问来源于stack exchange,提问作者iKingNinja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 02:17:27