一、什么是跨域?为什么会被拦截?
1. 浏览器的安全规则:同源策略
浏览器默认禁止 :一个域名下的前端网页,去请求另一个不同域名 / 端口 / 协议的后端接口。
只要下面 3 个有一个不一样,就是跨域:
- 协议(http /https)
- 域名(localhost / 127.0.0.1 / www.xxx.com)
- 端口(3000 / 8000 / 8080)
2. 最常见的场景
- 前端:
localhost:3000(React/Vue/ 小程序) - 后端:
localhost:8000(FastAPI)
端口不同 → 跨域 → 浏览器直接拦截请求你会在控制台看到经典报错:
plaintext
rust
Access to XMLHttpRequest at 'http://localhost:8000/xxx'
from origin 'http://localhost:3000' has been blocked by CORS policy.
二、CORSMiddleware 到底做了什么?
一句话:给后端接口添加允许跨域的响应头,告诉浏览器:这个请求是安全的,放行!
核心就是在 HTTP 响应里加一行:
plaintext
makefile
Access-Control-Allow-Origin: *
浏览器看到这行,就不再拦截。
三、逐行代码详细解析
完整代码
python
运行
ini
# 导入 FastAPI 的跨域中间件
from fastapi.middleware.cors import CORSMiddleware
# 创建 FastAPI 实例
app = FastAPI()
# 配置允许跨域
app.add_middleware(
CORSMiddleware, # 使用的中间件
allow_origins=["*"], # 允许哪些来源(域名/前端地址)
allow_credentials=True, # 允许携带Cookie
allow_methods=["*"], # 允许所有请求方法(GET/POST/PUT/DELETE)
allow_headers=["*"], # 允许所有请求头
)
逐参数解释
-
allow_origins=["*"]*= 允许所有来源跨域- 生产环境可以写死:
["https://your-frontend.com"]
-
allow_credentials=True- 允许前端带 cookie、token 跨域请求
-
allow_methods=["*"]- 允许所有 HTTP 方法:GET、POST、PUT、DELETE 等
-
allow_headers=["*"]- 允许所有请求头,比如
Authorization、Content-Type
- 允许所有请求头,比如
四、极简工作流程
- 前端(localhost:3000)→ 请求后端(localhost:8000)
- 浏览器发现跨域,先检查后端响应头
- 后端加了 CORSMiddleware → 响应头带上
允许跨域 - 浏览器看到允许 → 正常请求成功
五、为什么必须后端配置,前端不能解决?
跨域是浏览器的安全限制 ,不是后端限制,也不是前端限制。只有后端明确授权,浏览器才会放行。
六、最实用的两种配置
1. 开发环境(允许所有)
python
运行
ini
allow_origins=["*"]
2. 生产环境(只允许你的前端域名)
python
运行
ini
allow_origins=[
"https://www.your-app.com",
"https://app.your-app.com"
]
七、一句话总结
CORSMiddleware 就是给后端开 "跨域通行证" ,让前端(3000 端口)可以正常调用后端(8000 端口),解决浏览器的跨域拦截报错。
你只要记住
- 前后端端口不同 = 跨域
- 跨域会被浏览器拦截
- 加这段代码 = 后端允许跨域
- 浏览器看到允许 = 请求成功
需要我给你演示一个完整可运行的 FastAPI + 跨域 + 接口的最小 demo 吗?复制就能跑。
再详细一点
超深度详解 FastAPI CORSMiddleware
从浏览器同源策略底层 → 跨域触发条件 → 预检请求 → 中间件源码逻辑 → 每个参数底层含义 → 生产踩坑,全给你掰开讲透。
一、先底层:浏览器 同源策略 Same-Origin Policy
1. 什么是「源 Origin」
一个源由三部分组成:协议 + 域名 + 端口
- 协议:
http/https - 域名:
localhost/127.0.0.1/baidu.com - 端口:
8000/3000/8080
三个完全一致 = 同源****任意一个不同 = 跨域
2. 同源策略的目的
浏览器安全机制:防止恶意网站偷偷读取你登录态、Cookie、本地存储、接口数据。比如你登录了银行网站,恶意网页不能随便调银行接口偷数据。
3. 哪些行为受跨域限制
- Ajax / Fetch / Axios 请求后端接口
- 读取跨域 Cookie、LocalStorage
- 跨域 DOM 访问
普通 <img src="">、<script src="">、<link> 不受跨域限制,这是浏览器设计特例。
二、什么是 CORS?
CORS = Cross-Origin Resource Sharing 跨域资源共享 是 W3C 标准 ,用来合法绕过同源策略。
原理:**后端通过 HTTP 响应头,主动告诉浏览器:我允许某个前端源跨域访问我。**浏览器拿到合法响应头,就放行请求;没有就直接拦截,控制台报错。
三、为什么会出现跨域(经典场景)
前端:http://localhost:3000后端:http://localhost:8000
协议相同、域名相同、端口不同 → 跨域浏览器直接拦截前端的 Fetch/Axios 请求。
注意:跨域只限制浏览器 ,Postman、后端调后端、curl 命令完全不受跨域限制 。跨域不是后端不让你访问,是浏览器拦的。
四、浏览器两种跨域请求类型
1. 简单请求
满足全部条件:
- 请求方法:
GET/POST/HEAD - 请求头只有:
Accept、Accept-Language、Content-Language、Content-Type(仅三种值) - 没有自定义请求头(比如不带
Authorization: Bearer token)
流程:直接发真实请求,后端返回跨域响应头,浏览器判断是否放行。
2. 预检请求 Preflight(OPTIONS)
只要不满足简单请求,浏览器会先发一次 OPTIONS 预检请求 :问后端:我这个跨域请求允许吗?允许什么方法、什么头、带不带凭证?后端返回允许的头,浏览器才会发真实 POST/PUT/DELETE 请求。
常见触发预检:
- 带自定义请求头
Authorization Content-Type: application/json- PUT / DELETE / PATCH 方法
👉 FastAPI 的 CORSMiddleware 自动帮你处理 OPTIONS 预检请求,不用自己写接口处理 OPTIONS。
五、代码本体解析
python
运行
javascript
from fastapi.middleware.cors import CORSMiddleware
- 来自
starlette底层的跨域中间件(FastAPI 基于 Starlette) - 作用:拦截所有进入后端的 HTTP 请求
- 自动识别跨域 Origin
- 自动在响应头插入 CORS 相关字段
- 自动响应 OPTIONS 预检请求
完整配置模板:
python
运行
ini
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
max_age=3600
)
六、逐个参数 底层深度解释
1. allow_origins
作用:允许哪些前端源访问后端
["*"]:允许所有任意域名跨域- 指定域名:
["http://localhost:3000", "https://xxx.com"]
底层响应头:
plaintext
makefile
Access-Control-Allow-Origin: 对应域名
重点坑点 :如果 allow_credentials=True,**不能用 allow_origins=["*"]**浏览器规则:允许携带凭证时,禁止使用通配符 *,必须写死具体域名。
2. allow_credentials=True
作用:允许跨域请求携带
- Cookie
- HttpOnly Cookie
- 认证头部、会话凭证
底层响应头:
plaintext
yaml
Access-Control-Allow-Credentials: true
登录态、Token 放 Cookie 里跨域,必须开这个。
3. allow_methods=["*"]
允许跨域使用哪些 HTTP 请求方法GET POST PUT DELETE PATCH OPTIONS
底层响应头:
plaintext
makefile
Access-Control-Allow-Methods: GET,POST,PUT,DELETE...
4. allow_headers=["*"]
允许前端带哪些自定义请求头 比如:Authorization、X-Token、Content-Type
底层响应头:
plaintext
makefile
Access-Control-Allow-Headers: Authorization,Content-Type...
前端传 Token 鉴权,必须配置这个。
5. max_age=3600
预检请求缓存时间(单位秒)浏览器 1 小时内不用再发 OPTIONS 预检,直接请求,提升性能。
七、中间件底层工作流程
-
前端发起跨域请求
-
请求先经过 FastAPI 中间件层
CORSMiddleware -
中间件提取请求头
Origin -
校验这个 Origin 是否在
allow_origins允许列表 -
如果允许:
- 在响应头自动塞入所有 CORS 字段
- 若是 OPTIONS 预检请求,直接返回 200 空响应,不进入业务接口
-
浏览器拿到响应头,校验通过 → 放行请求
八、开发环境 vs 生产环境 标准写法
开发环境(省事)
python
运行
ini
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
缺点 :不能同时开 allow_credentials=True
生产环境(规范安全)
python
运行
ini
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000",
"https://your-frontend.com"
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
max_age=3600
)
- 不使用
* - 精确放行前端域名
- 限定请求方法、请求头
- 开启凭证,支持 Cookie/Token 登录
九、核心总结一句话
- 跨域是浏览器同源策略限制,不是后端限制;
CORSMiddleware是后端主动给响应头授权,告诉浏览器允许跨域;- 自动处理 OPTIONS 预检请求,不用手写接口;
- 开发可用
*,生产必须指定具体域名 ,开凭证就不能用*; - 带
Authorization、JSON 请求、增删改接口,都会触发预检,靠这个中间件自动处理。
CORS 跨域完整时序流程图 + 文字逐帧拆解
一、先分清两种请求:简单请求 / 预检请求
1. 简单请求 时序图
plaintext
bash
前端浏览器(localhost:3000)
↓ 发起 GET/普通POST
↓ 无自定义头、无application/json
浏览器拦截校验:发现跨域
↓
向后端直接发起真实请求
↓
FastAPI + CORSMiddleware
↓
中间件自动添加CORS响应头:
Access-Control-Allow-Origin
↓
返回响应给浏览器
↓
浏览器校验响应头合法 → 放行 → 前端拿到数据
文字流程拆解
- 前端
fetch/axios发简单 GET / 普通 POST - 浏览器识别:协议 / 域名 / 端口不一致 → 跨域
- 直接发真实请求,不先发 OPTIONS
- 请求到达 FastAPI,先走
CORSMiddleware中间件 - 中间件匹配
Origin,自动塞入跨域响应头 - 浏览器拿到响应头,核对允许规则
- 校验通过,把数据交给前端业务代码
2. 预检请求 (OPTIONS) 标准时序(90% 项目都是这种)
触发场景:
- 带
Authorization令牌头 Content-Type: application/json- PUT / DELETE / PATCH 方法
plaintext
scss
前端浏览器(localhost:3000)
↓
浏览器先拦截:这不满足简单请求 → 必须预检
↓ 自动先发 OPTIONS 预检请求
FastAPI CORSMiddleware 拦截
↓
中间件校验 Origin、方法、请求头是否在白名单
↓
返回 200 + 一串CORS预检响应头
(Allow-Origin / Allow-Methods / Allow-Headers / Max-Age)
↓
浏览器收到预检结果:合法
↓ 间隔极短,立刻发起【真实业务请求】(POST/PUT/DELETE)
后端正常执行业务接口逻辑
↓
返回业务数据 + 跨域响应头
↓
浏览器放行,前端拿到真实结果
逐步拆解
- 前端要发 JSON 格式、带 Token、非 GET 请求
- 浏览器不会直接发业务请求 ,先自作主张发一次 OPTIONS 预检
- 请求到达 FastAPI,
CORSMiddleware接管 OPTIONS 请求 - 中间件不进你的业务接口,直接返回 200 和预检头
- 浏览器确认:域名、方法、请求头都被允许
- 再发起真正的业务 POST/PUT 请求
- 后端正常处理接口,返回数据
- 浏览器再次校验响应头,放行给前端
二、CORSMiddleware 在 FastAPI 整体链路位置
plaintext
markdown
客户端请求
↓
FastAPI 中间件层
↳ CORSMiddleware(最先拦截)
↓
路由匹配
↓
接口函数执行业务逻辑
↓
响应原路返回
↓
中间件加上CORS响应头
↓
浏览器
核心关键点:
- 跨域校验在中间件层就做完了
- OPTIONS 预检请求根本不会进到你写的接口代码,中间件直接拦截并返回
三、必记致命坑(对应你配置参数)
allow_credentials=True时绝对不能 用allow_origins=["*"]浏览器强制规则:允许 Cookie / 凭证 就禁止通配符,必须写死具体域名。- 前端带
Authorization: Bearer xxx必然触发 OPTIONS 预检,必须靠中间件自动处理,自己手写接口很难兼容。 max_age=3600预检结果缓存 1 小时,这段时间内同类型请求不再发 OPTIONS,提升性能。
四、极简口诀背下来
-
三要素不同就是跨域:协议、域名、端口
-
跨域是浏览器拦的,不是后端不让访问
-
普通 GET 走简单请求 ,带 Token/JSON 走预检 OPTIONS
-
CORSMiddleware 做三件事:
- 识别跨域 Origin
- 自动响应 OPTIONS 预检
- 给响应头加跨域授权字段
-
开发可用
*,生产写死域名 ,开凭证不能用*
FastAPI CORS 生产 + 开发 两套标准模板(直接复制即用)
一、开发环境模板(本地调试用)
特点:随便跨域、省事、不用写前端域名
python
运行
ini
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 开发环境 CORS 配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 允许所有前端源
allow_methods=["*"], # 允许所有请求方法
allow_headers=["*"], # 允许所有请求头
max_age=3600 # 预检缓存1小时
)
⚠️ 注意:这套不能加 allow_credentials=True ,一加就报错,浏览器不允许 * + 带凭证。
二、生产环境标准模板(企业级规范,推荐一直用)
特点:安全、精准放行、支持 Cookie/Token/ 登录凭证、兼容预检请求
python
运行
ini
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 生产环境 允许的前端域名白名单
ALLOW_ORIGINS = [
"http://localhost:3000",
"http://localhost:5173",
"https://your-frontend.com",
"https://admin.your-frontend.com"
]
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOW_ORIGINS,
allow_credentials=True, # 允许跨域带 Cookie、Token、会话凭证
allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
allow_headers=["Authorization", "Content-Type", "X-Token"],
max_age=3600
)
三、关键参数固定解释(以后不用再查)
allow_origins只填你真实前端地址 ,不写*,防止恶意网站盗用接口。allow_credentials=True必须开:
- 前端带
Authorization令牌 - 登录存 Cookie、会话保持开了就不能用
["*"]
allow_methods只开放业务用到的请求方法,最小权限安全原则。allow_headers只放行认证头、内容类型头,不要无脑全开放。max_age=3600缓存 OPTIONS 预检结果,1 小时内不再重复发预检,提升接口速度。
四、最佳实践写法(适配开发 / 生产自动切换)
python
运行
ini
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import os
app = FastAPI()
# 环境变量区分开发/生产
ENV = os.getenv("ENV", "dev")
if ENV == "dev":
# 开发
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
max_age=3600
)
else:
# 生产
ALLOW_ORIGINS = [
"https://your-frontend.com",
"https://admin.your-frontend.com"
]
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOW_ORIGINS,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
allow_headers=["Authorization", "Content-Type"],
max_age=3600
)
以后新建 FastAPI 项目,直接复制这段,不用再手写 CORS。