ASP.NET Core 跨域資源存取(CORS)設定
ASP.NET Core 跨域資源存取(CORS)設定
前言
前後端分離寫久了,總會遇到那個熟悉的紅字:
Access to fetch at 'https://api.example.com' from origin 'https://www.example.com'
has been blocked by CORS policy.
前端工程師看到這行,第一反應通常是轉頭問後端:「你 CORS 開了沒?」
後端工程師則是一臉無辜:「我本機明明可以啊。」
問題就在這裡——CORS 是瀏覽器的安全機制,本機用 Postman、Swagger 打都不會擋,
只有真的從另一個網域用瀏覽器發請求才會觸發。所以「我本機可以」這句話在 CORS 面前完全沒有意義。

這篇就把 ASP.NET Core 的 CORS 設定一次講清楚,並把它封裝成一個擴充方法,
搭配設定檔的允許清單,讓 Program.cs 只剩乾淨的一行。
觀念釐清
CORS(Cross-Origin Resource Sharing)解決的是「瀏覽器禁止網頁向不同來源(origin)發送請求」這件事。
所謂「不同來源」,只要 協定、網域、port 任一個不一樣就算:
| 前端來源 | 後端 API | 同源? |
|---|---|---|
https://www.example.com | https://www.example.com/api | ✅ 同源 |
https://www.example.com | https://api.example.com | ❌ 網域不同 |
http://localhost:5173 | http://localhost:5000 | ❌ port 不同 |
http://localhost | https://localhost | ❌ 協定不同 |
重點:CORS 是後端發給瀏覽器的許可,由後端在回應標頭告訴瀏覽器「我允許這個來源」,
而不是後端去擋誰。擋你的人從頭到尾都是瀏覽器。
設定允許清單
直接沿用 ASP.NET Core 內建的 AllowedHosts 設定鍵,多個來源用分號 ; 分隔:
{
"AllowedHosts": "https://www.example.com;https://admin.example.com"
}開發環境放本機前端位址即可:
{
"AllowedHosts": "http://localhost:5173;http://localhost:8080"
}想全部放行就填
"*"(或乾脆留空 / 不設定),下面的程式會自動 fallback 成允許所有來源。
封裝成擴充方法
把整段 CORS 設定收進一個擴充方法,順手定義一個對應的設定模型:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Riyar.API.Base.Extensions;
public static class CorsExtension
{
/// <summary> 設定跨域資源存取 </summary>
public static IServiceCollection AddCorsDefaultPolicy(this IServiceCollection services, IConfiguration configuration)
{
// 從根層級綁定 AllowedHosts 設定
services.Configure<CorsSettings>(configuration);
// 讀取允許列表,若為 null 或空白則預設允許所有來源
string? allowedHosts = services.BuildServiceProvider()
.GetRequiredService<IOptionsMonitor<CorsSettings>>()
.CurrentValue.AllowedHosts;
string[] corsOrigins = string.IsNullOrWhiteSpace(allowedHosts)
? ["*"]
: allowedHosts.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
// 加入具有預設原則的 CORS
services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
if (corsOrigins.Contains("*"))
{
// 萬用字元:允許所有來源(僅建議用於開發或公開唯讀 API)
policy.SetIsOriginAllowed(_ => true);
}
else
{
policy.WithOrigins(corsOrigins);
}
policy.AllowAnyHeader();
policy.AllowAnyMethod();
policy.AllowCredentials();
});
});
return services;
}
}
public class CorsSettings
{
/// <summary> 允許的跨域來源,多個來源以 ';' 分隔,使用 '*' 允許所有來源 </summary>
public string? AllowedHosts { get; set; }
}於是 Program.cs 只要一行:
var builder = WebApplication.CreateBuilder(args);
// 註冊 CORS 預設原則
builder.Services.AddCorsDefaultPolicy(builder.Configuration);
var app = builder.Build();
// 啟用預設 CORS 原則
app.UseCors();
app.Run();幾個關鍵設計:
| 重點 | 說明 |
|---|---|
services.Configure<CorsSettings>(configuration) | 直接綁根層級設定,所以對應的是最外層的 AllowedHosts |
分號 Split(';', ...) | 用 RemoveEmptyEntries | TrimEntries,自動去除空項與前後空白,貼設定不怕手殘多打空格 |
IsNullOrWhiteSpace → ["*"] | 沒設定就 fallback 成允許所有來源,本機開發不用特別設定也能跑 |
SetIsOriginAllowed(_ => true) | 萬用字元的正確寫法(理由見下節) |
為什麼設定鍵直接用
AllowedHosts?因為它本來就是 ASP.NET Core 既有的設定鍵
(原本給 Host Filtering 用),格式天生就是分號分隔的字串,沿用它最省事、也最直覺。
一個常見的陷阱:萬用字元 + 認證
注意上面用的是 SetIsOriginAllowed(_ => true) 而不是 AllowAnyOrigin(),這不是隨便寫的。
很多人第一次設定會直接這樣:
policy.AllowAnyOrigin(); // 等同 *
policy.AllowCredentials(); // 允許帶 cookie / token然後就會踩到這個錯誤:
The CORS protocol does not allow specifying a wildcard (any) origin and credentials at the same time.
這是 CORS 規範的硬性限制:只要允許攜帶認證資訊(AllowCredentials),就不能用萬用字元來源。
SetIsOriginAllowed(_ => true) 會在回應中「回填實際請求的來源」,等於動態指定具體 origin,
因此能繞過這個限制:
::: code-group
policy.SetIsOriginAllowed(_ => true);
policy.AllowCredentials();policy.AllowAnyOrigin();
policy.AllowCredentials();:::
雖然技術上可行,但「允許所有來源 + 帶認證」在正式環境是很危險的組合,
等於把任何網站都當成可信來源。正式環境請乖乖在AllowedHosts列白名單。

中介軟體順序
app.UseCors() 的擺放位置會影響行為,務必放在路由之後、認證授權之前:
app.UseRouting();
app.UseCors(); // ← 要在這裡
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();順序錯了最常見的症狀是:預檢請求(OPTIONS)被認證中介軟體攔下來,
回了 401,前端就看到 CORS 錯誤了。
小結
- CORS 是瀏覽器機制,本機工具測不出來,要用瀏覽器跨網域才會觸發。
- 把設定封裝成
AddCorsDefaultPolicy擴充方法,Program.cs只剩一行,乾淨好維護。 - 允許清單沿用
AllowedHosts,分號分隔;沒設定就自動放行所有來源。 - 要帶認證(cookie / token)就不能用萬用字元,請用
WithOrigins或SetIsOriginAllowed。 UseCors()要放在認證授權之前。