Appearance
45|CORS 配置与跨域处理
前端开发服务器(如 Vite,端口 5173)和后端 API(端口 8000)运行在不同端口上,浏览器认为这是两个不同的"源"(origin)。前端用 fetch 调用后端接口时,浏览器会阻止响应——这就是同源策略(Same-Origin Policy)。CORS(Cross-Origin Resource Sharing)是一种让服务器声明"允许哪些源访问我"的机制。
一、同源策略
两个 URL 同源,当且仅当协议、域名、端口都相同:
| URL 1 | URL 2 | 是否同源 | 原因 |
|---|---|---|---|
http://a.com:8000/api | http://a.com:8000/data | 是 | 完全相同 |
http://a.com:8000/api | http://a.com:5173/ | 否 | 端口不同 |
http://a.com/api | https://a.com/api | 否 | 协议不同 |
http://a.com/api | http://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 头:
- 后端
allow_credentials=True - 后端
allow_origins不能是*,必须明确列出允许的源 - 前端
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排查步骤:
- 确认 CORS 中间件已添加(不是只 import 没 add_middleware)
- 确认
allow_origins包含前端的完整 URL(包括端口) - 确认预检请求(OPTIONS)也返回了正确的 CORS 头
- 检查是否有代理(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 中间件。