Skip to content

45|CORS 配置与跨域处理

前端开发服务器(如 Vite,端口 5173)和后端 API(端口 8000)运行在不同端口上,浏览器认为这是两个不同的"源"(origin)。前端用 fetch 调用后端接口时,浏览器会阻止响应——这就是同源策略(Same-Origin Policy)。CORS(Cross-Origin Resource Sharing)是一种让服务器声明"允许哪些源访问我"的机制。

一、同源策略

两个 URL 同源,当且仅当协议、域名、端口都相同:

URL 1URL 2是否同源原因
http://a.com:8000/apihttp://a.com:8000/data完全相同
http://a.com:8000/apihttp://a.com:5173/端口不同
http://a.com/apihttps://a.com/api协议不同
http://a.com/apihttp://b.com/api域名不同

同源策略限制的是浏览器读取跨域响应,不是发送请求。请求实际上已经发到服务端,只是浏览器把响应拦截了。

二、简单请求与预检请求

简单请求

满足以下条件的请求不需要预检:

  • 方法:GET、HEAD、POST
  • Content-Type:application/x-www-form-urlencoded、multipart/form-data、text/plain
  • 没有自定义头

简单请求直接发送,服务端如果返回 Access-Control-Allow-Origin 头,浏览器就放行。

预检请求(Preflight)

不满足简单请求条件的(如 PUT、DELETE、自定义头、Content-Type: application/json),浏览器会先发送一个 OPTIONS 请求"询问"服务器是否允许:

OPTIONS /api/servers HTTP/1.1
Origin: http://localhost:5173
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization

服务器返回允许的规则:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

浏览器确认允许后,再发送真正的请求。

三、FastAPI CORS 配置

python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5173", "https://ops.example.com"],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Content-Type", "Authorization", "X-Request-ID"],
    max_age=86400,
)
参数作用
allow_origins允许访问的源列表,["*"] 允许所有(不推荐)
allow_credentials允许携带 Cookie 和 Authorization 头
allow_methods允许的 HTTP 方法
allow_headers允许的请求头
max_age预检结果缓存时间(秒)

开发环境简化配置

python
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

开发阶段可以放宽限制,但生产环境必须明确指定 allow_origins,不能用 *

四、Credentials 的注意事项

如果前端需要携带 Cookie 或 Authorization 头:

  1. 后端 allow_credentials=True
  2. 后端 allow_origins 不能是 *,必须明确列出允许的源
  3. 前端 fetch 设置 credentials: "include"
js
fetch("http://localhost:8000/api/me", {
  credentials: "include",   // 发送 Cookie
  headers: { "Authorization": `Bearer ${token}` },
});

五、常见错误

配置了 CORS 但前端仍然报错

Access to fetch at 'http://localhost:8000/api/servers' from origin
'http://localhost:5173' has been blocked by CORS policy

排查步骤:

  1. 确认 CORS 中间件已添加(不是只 import 没 add_middleware)
  2. 确认 allow_origins 包含前端的完整 URL(包括端口)
  3. 确认预检请求(OPTIONS)也返回了正确的 CORS 头
  4. 检查是否有代理(Nginx)过滤了 CORS 头

allow_origins 用通配符同时开 credentials

python
# 错误:浏览器会拒绝,这是 CORS 规范限制
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
)

# 正确:credentials=True 时必须明确指定源
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5173"],
    allow_credentials=True,
)

忘记处理 OPTIONS 预检

如果中间件或代理把 OPTIONS 请求拦截了,预检会失败。确保 OPTIONS 请求能到达 FastAPI 的 CORS 中间件。