CORS策略不生效的主因是中间件顺序错误或WithOrigins()配置不规范:必须在UseRouting之后、认证授权之后、MapControllers之前调用UseCors(),且WithOrigins()需写完整协议+域名+端口,禁用通配符。

必须用 WithOrigins() 明确列出完整协议+域名+端口,不能写 "*",也不能拼接通配符子域(如 "https://*.example.com")——否则策略不生效或启动报错。
为什么 AddCors() 配了白名单却没生效?
最常见原因是中间件顺序错位:app.UseCors("MyPolicy") 必须在 app.UseRouting() 之后、app.UseAuthentication() 和 app.UseAuthorization() 之后、app.MapControllers() 或 app.MapGet() 之前。顺序错一位,CORS 就完全不触发。
- 如果用了 Minimal API(比如
app.MapGet("/api/data", ...)),UseCors()必须在这类映射调用之前,否则路由匹配不到中间件 - 策略名大小写敏感:注册时是
"MyPolicy",UseCors("mypolicy")会静默失败 -
AddCors()只是注册策略,不调用UseCors()就等于没启用
WithOrigins() 的字符串必须带协议、端口,且不能含通配符
浏览器发来的 Origin 头是完整字符串(如 "https://admin.example.com:8080"),WithOrigins() 匹配时严格比对,不支持正则或模糊匹配。
- ✅ 正确写法:
WithOrigins("https://localhost:5173", "https://app.example.com") - ❌ 错误写法:
WithOrigins("localhost:5173")(缺协议)、WithOrigins("https://*.example.com")(通配符无效)、WithOrigins("*")(与凭据冲突) - 前端用
file://协议打开 HTML 时,Origin 是null,WithOrigins()不接受null,此时必须换用SetIsOriginAllowed
需要支持子域或动态白名单?改用 SetIsOriginAllowed
当要匹配 app1.example.com、app2.example.com,或从配置中心加载域名列表时,WithOrigins() 不够用,必须用 SetIsOriginAllowed 自定义判断逻辑。
- 它接收一个
Func<string, bool>,每次预检(OPTIONS)请求都会执行,别在里面查数据库或远程 HTTP 请求 - 建议提前把白名单加载进内存字典或 HashSet,再用
origin.EndsWith(".example.com")或安全的正则匹配 - 匹配前最好用
Uri.TryCreate(origin, UriKind.Absolute, out _)校验合法性,防止恶意构造的 Origin 绕过 - 注意:一旦用了
SetIsOriginAllowed,WithOrigins()和AllowAnyOrigin()就会被忽略
带 Cookie 或 Authorization 时,AllowCredentials() 和白名单强绑定
只要前端请求设置了 credentials: "include"(或 withCredentials = true),后端就必须同时满足两个条件:明确指定源(不能是 "*")、调用 AllowCredentials()。
- 否则 .NET 启动直接抛异常:
System.InvalidOperationException: When requesting credentials, the 'Access-Control-Allow-Origin' header cannot be '*' -
AllowCredentials()和AllowAnyOrigin()不能共存,否则启动失败 - 浏览器会拒绝响应,即使后端返回了
Access-Control-Allow-Origin: "*",控制台错误是:Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'
真正容易被忽略的是:开发时习惯性用 AllowAnyOrigin() 调试,上线前不仅得改成 WithOrigins(),还得同步检查是否漏掉了 AllowCredentials() —— 这个组合一旦出错,不是 500,而是预检直接被浏览器拦截,连服务端日志都看不到请求痕迹。


















