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

YARP反向代理场景下如何调整请求头解决CORS跨域错误?

问题场景

当前部署了基于YARP的反向代理站点,入口域名为test.a.com,负责接收所有传入请求并转发到对应目标网站,默认规则下所有请求会转发到旧站点https://test-www.a.com/,基础转发功能运行正常。
YARP配置如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ReverseProxy": {
    "Routes": {
      "server": {
        "ClusterId": "old-site",
        "Match": {
          "Path": "{**catch-all}"
        }
      },
      "azure": {
        "ClusterId": "new-site",
        "Match": {
          "Path": "yarpb"
        }
      }
    },
    "Clusters": {
      "old-site": {
        "Destinations": {
          "server": {
            "Address": "https://test-www.a.com/"
          }
        }
      },
      "new-site": {
        "Destinations": {
          "yarpb": {
            "Address": "https://example.com/"
          }
        }
      }
    }
  }
}

目前Umbraco CMS后台(访问路径test.a.com/umbraco)加载异常:后台运行时会从app_plugins目录拉取资源文件,部分资源拉取请求触发CORS跨域校验,原始HTML请求触发重定向时直接抛出跨域异常:
CORS跨域校验报错截图
重定向引发跨域异常截图

已尝试在代码中添加app.UseCors(x => x.AllowAnyOrigin());配置允许所有来源跨域,但未生效:从YARP运行日志看代理请求最终返回200响应,但浏览器端提示405错误,判断CORS中间件在YARP完成请求重定向后才执行,未对代理响应生效。
相关运行日志片段如下:

2022-06-21T17:48:02.6237461+02:00   INFO    [Yarp.ReverseProxy.Forwarder.HttpForwarder] [Forwarding]    Proxying to https://test-www.a.com/App_Plugins/RJP.MultiUrlPicker/MultiUrlPicker.html HTTP/2 RequestVersionOrLower no-streaming
2022-06-21T17:48:02.6255128+02:00   INFO    [Yarp.ReverseProxy.Forwarder.HttpForwarder] [ResponseReceived]  Received HTTP/2.0 response 301.
2022-06-21T17:48:02.6256100+02:00   INFO    [ReverseProxy.Middleware.RequestResponseLoggerMiddleware]   [LogRequest]    https://test.a.com/App_Plugins/RJP.MultiUrlPicker/MultiUrlPicker.html proxied to https://test-www.a.com//App_Plugins/RJP.MultiUrlPicker/MultiUrlPicker.html
2022-06-21T17:48:02.6273081+02:00   INFO    [Yarp.ReverseProxy.Forwarder.HttpForwarder] [ResponseReceived]  Received HTTP/2.0 response 200.

项目原有Program.cs代码如下:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
builder.Services.AddLogging(x =>
{
    x.AddJsonConsole();
    x.AddFile($"logs/app-{DateTime.UtcNow:yyyyMMddHHmmss}.log", append: true);
});

var app = builder.Build();


app.UseStaticFiles();
app.UseRouting();
app.UseCors(x => x.AllowAnyOrigin());

app.MapReverseProxy(proxyPipeline =>
{
    proxyPipeline.UseRequestResponseLogging();
});

app.Run();
问题根因
  • 中间件执行顺序错误:ASP.NET Core中间件严格按注册顺序执行,原有代码中CORS中间件注册在YARP代理映射外层,YARP转发后拿到的上游301重定向响应会直接返回给客户端,不会经过外层CORS中间件处理,导致跨域头缺失。
  • 路径拼接错误触发不必要重定向:从运行日志可见,转发后的目标地址出现双斜杠https://test-www.a.com//App_Plugins/...,上游Umbraco站点识别到路径异常会返回301重定向到正确路径,重定向响应的Location头直接指向源站域名,浏览器收到后直接判定跨域。
  • 响应头未做代理层转换:上游站点返回的跨域相关头、重定向地址头没有经过代理层修正就直接透传,和代理入口域名不匹配,触发浏览器跨域拦截。
  • OPTIONS预检请求未正确处理:浏览器跨域请求前会发送OPTIONS预检请求,原有逻辑将该请求转发到源站,源站未配置对应跨域响应时会返回405错误,和浏览器端报错现象一致。
修复方案

1. 调整中间件顺序,将CORS逻辑嵌入YARP代理管道

不要仅在代理外层注册CORS中间件,需要把CORS处理加入YARP自身的代理管道,保证代理返回的所有响应(包括301重定向响应)都能被正确添加跨域头。

2. 配置YARP请求/响应转换规则

  • 修复路径拼接时的双斜杠问题,避免触发上游不必要的301重定向
  • 重写上游3xx响应的Location头,将源站域名替换为代理入口域名,避免跳转暴露源站地址
  • 统一在代理层添加跨域响应头,覆盖上游可能返回的错误跨域配置

3. 全局拦截处理OPTIONS预检请求

在代理层直接响应OPTIONS预检请求,不要转发到源站,避免源站返回405错误。

修改后的完整Program.cs代码如下:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    // 注册请求/响应转换规则
    .AddTransforms(transformBuilder =>
    {
        // 移除转发路径开头的斜杠,避免和集群地址结尾的斜杠拼接成双斜杠
        transformBuilder.AddPathRemovePrefix("/");
        // 重写3xx重定向的Location头,替换源站域名为代理入口域名
        transformBuilder.AddResponseHeader("Location", value =>
        {
            if (value.StartsWith("https://test-www.a.com/", StringComparison.OrdinalIgnoreCase))
            {
                return value.Replace("https://test-www.a.com/", "https://test.a.com/");
            }
            return value;
        }, append: false);
        // 统一写入跨域响应头,始终覆盖上游返回的同名头
        transformBuilder.AddResponseHeader("Access-Control-Allow-Origin", "*", append: false);
        transformBuilder.AddResponseHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS", append: false);
        transformBuilder.AddResponseHeader("Access-Control-Allow-Headers", "*", append: false);
    });

builder.Services.AddLogging(x =>
{
    x.AddJsonConsole();
    x.AddFile($"logs/app-{DateTime.UtcNow:yyyyMMddHHmmss}.log", append: true);
});

// 注册CORS服务
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader();
    });
});

var app = builder.Build();

app.UseStaticFiles();
app.UseRouting();
// 外层CORS中间件处理非代理请求
app.UseCors();

// 全局拦截OPTIONS预检请求,直接返回200
app.Use(async (context, next) =>
{
    if (context.Request.Method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase))
    {
        context.Response.StatusCode = StatusCodes.Status200OK;
        return;
    }
    await next();
});

app.MapReverseProxy(proxyPipeline =>
{
    // 代理管道内添加CORS中间件,保证所有代理响应都经过跨域处理
    proxyPipeline.UseCors();
    proxyPipeline.UseRequestResponseLogging();
});

app.Run();

内容的提问来源于stack exchange,提问作者J.Down

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:36:18