导语 开发 AI 网页对话、前端 RAG 知识库、数字人交互页面时,浏览器控制台高频出现No 'Access-Control-Allow-Origin'跨域报错:Network 面板明明能拿到接口 200 返回数据,但 JS 代码无法读取结果,流式 SSE 打字直接中断。很多新手只加单一跨域响应头,忽略 OPTIONS 预检请求、SSE 长连接、Token 鉴权场景导致线上故障。本期拆解浏览器同源安全底层逻辑,区分简单 / 预检跨域请求,给出 FastAPI、Nginx、Ollama、vLLM 四套可直接复制跨域配置,梳理 AI 项目专属跨域踩坑点与生产安全规范。
一、CORS 基础定义与同源策略底层安全逻辑通俗类比同源策略 = 小区门禁制度;
CORS(跨域资源共享)= 物业发放临时通行许可。
浏览器默认同源安全规则:前端页面域名、协议、端口三者必须完全一致,才能正常读取后端接口数据;三者任一不同即为跨域,浏览器会拦截 JS 获取响应体。
底层安全目的:防范跨站请求伪造(CSRF),防止恶意网页窃取你浏览器内登录 Cookie、AI 鉴权 Token。 同源三要素(全部一致才算同域)
- 协议:http /https
- 域名:localhost、ai.domain.com
- 端口:80、443、8000(vLLM 默认推理端口)
示例:前端
https://web.ai.com 调用http://123.45.67:8000/v1/chat → 协议、域名、端口全部不同,标准跨域场景。
核心真相:跨域拦截发生在浏览器,不是后端后端接口正常接收请求、完整返回 200 数据;浏览器拿到响应后校验Access-Control-Allow-Origin头部,无合法许可直接丢弃返回内容,前端 JS 读取失败。
抓包能看到返回数据、控制台报红报错,是 CORS 最典型特征。
二、两类跨域请求:简单请求 vs OPTIONS 预检请求1 简单请求(无预检,一步完成)同时满足全部条件才不会触发 OPTIONS 预检:
- 请求方法仅 GET/HEAD/POST
- Content-Type 仅限
application/x-www-form-urlencoded、multipart/form-data、text/plain
- 无自定义请求头(无 Authorization 鉴权 Token、无自定义标识)
普通静态页面、文件下载多为简单请求,仅需配置基础跨域头即可。
2 预检请求(复杂请求,AI 接口 99% 触发)只要满足任意一条,浏览器先发 OPTIONS 预检询问服务器权限,通过后再发起真实对话请求:
- 请求方法 PUT/DELETE/PATCH;
- Content-Type 为
application/json(LLM 对话标准请求格式);
- 携带
Authorization、X-Token等自定义鉴权头部。
线上高频踩坑仅处理 POST 真实请求,未返回 OPTIONS 预检跨域头,预检直接 403,对话完全无法发起。
三、CORS 核心响应头完整释义
Access-Control-Allow-Origin:允许访问的前端域名,生产禁止*通配符携带 Cookie/Token;
Access-Control-Allow-Methods:放行 GET/POST/OPTIONS 等请求方法;
Access-Control-Allow-Headers:放行鉴权 Token、Content-Type 自定义头部;
Access-Control-Allow-Credentials:是否允许携带 Cookie、鉴权 Token;
Access-Control-Max-Age:预检 OPTIONS 缓存时长,减少重复预检;
Access-Control-Expose-Headers:允许 JS 读取后端自定义返回头(流式、日志场景必备)。
关键红线规则Access-Control-Allow-Credentials: true开启鉴权时,Access-Control-Allow-Origin不能写*,必须填写精准前端域名,否则浏览器直接拦截。
四、AI 项目四类落地完整跨域配置方案 1 FastAPI /vLLM Python 后端内置 CORS(开发环境)适用于 FastAPI 网关、vLLM OpenAI 兼容接口,全局中间件一键开启: from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 生产填写前端精准域名,开发临时用["*"]
origins = [
"https://chat.ai-domain.com",
"http://localhost:5173" # 本地前端调试
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True, # 支持Token鉴权
allow_methods=["GET", "POST", "OPTIONS"],
allow_headers=["*"],
max_age=1728000
)
vLLM 启动配套:无需额外代码,FastAPI 中间件全局生效。 方案 2 Nginx 反向代理标准跨域(生产环境首选,含 SSE 流式)兼顾跨域、SSL、SSE 长连接缓冲关闭,处理 OPTIONS 预检: server {
listen 443 ssl;
server_name api.ai-domain.com;
ssl_certificate cert/fullchain.pem;
ssl_certificate_key cert/privkey.pem;
location /v1/chat/completions {
# 处理OPTIONS预检请求
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin "https://chat.ai-domain.com" always;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS always;
add_header Access-Control-Allow-Headers Authorization,Content-Type always;
add_header Access-Control-Allow-Credentials true always;
add_header Access-Control-Max-Age 1728000;
return 204;
}
# 真实业务请求跨域头
add_header Access-Control-Allow-Origin "https://chat.ai-domain.com" always;
add_header Access-Control-Allow-Credentials true always;
add_header Access-Control-Expose-Headers Data always;
# SSE流式必备配置
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set Connection "";
proxy_buffering off;
proxy_read_timeout 3600s;
}
}
核心关键字always:无论 200/403/500 任何状态码,都输出跨域头部,报错场景不会丢失许可。 方案 3 Ollama 本地模型跨域配置环境变量全局放行前端域名: # Linux/macOS终端临时生效
export OLLAMA_ORIGINS=https://localhost:5173,https://chat.ai-domain.com
ollama serve
# 永久写入环境变量
echo "export OLLAMA_ORIGINS=*" >> ~/.bashrc
source ~/.bashrc
方案 4 前端开发代理(仅本地调试,不线上使用)Vite 前端配置,同源转发规避跨域限制: // vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8000',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
})
五、AI 流式 SSE 专属跨域特殊处理
- 必须添加
Access-Control-Expose-Headers,前端 JS 才能读取 SSE 返回 data 分片;
- Nginx 禁止开启
proxy_buffering缓冲,否则流式卡顿 + 跨域偶发拦截;
3 OPTIONS 预检需独立返回 204 空响应,不能转发至推理后端。
六、线上高频 CORS 报错根因与修复故障 1 控制台报跨错,Network 接口 200 有返回数据根因:后端未返回合法Access-Control-Allow-Origin;
修复:Nginx 或后端中间件添加精准域名跨域头,携带 always 标识。 故障 2 POST 对话报错,GET 静态页面正常根因:POST 携带 JSON 触发 OPTIONS 预检,未处理 OPTIONS 请求;
修复 Nginx 单独拦截 OPTIONS,直接返回跨域 204,不转发 vLLM。 故障 3 配置allow_origins=["*"]仍报错根因:开启Allow-Credentials: true鉴权,禁止全局*;
修复替换为前端完整域名。 故障 4 SSE 流式对话中途断开,偶发跨域拦截根因 Nginx 缓冲开启,长连接响应头丢失;
修复proxy_buffering off,延长读取超时。 故障 5 本地前端调试正常,上线生产报错根因开发通配符*,生产未替换真实线上域名;
修复分开发 / 生产两套域名配置。
七、生产环境 CORS 安全规范(禁止踩坑)
- 线上禁用
Access-Control-Allow-Origin: *,精准配置业务前端域名;
2 必须单独处理 OPTIONS 预检请求,减少推理服务无效请求;
3 携带 API Token 场景开启Allow-Credentials,严格限制来源;
4 禁止开放Allow-Methods: *,仅放行业务所需 GET/POST/OPTIONS;
5 反向代理层统一管理跨域规则,推理后端无需重复配置。
八、新手高频认知误区澄清误区 1 后端返回数据 = 前端能读取纠正拦截发生在浏览器,后端正常返回不代表 JS 可获取。 误区 2 只配置 POST 接口跨域即可纠正 JSON 对话触发 OPTIONS 预检,不处理预检请求直接拦截。 误区 3 线上用*通配符省事纠正携带 Token/ Cookie 时浏览器直接拒绝,存在 CSRF 安全风险。 误区 4 前端加 header 能绕过 CORS纠正同源策略是浏览器底层安全限制,前端无法解除。 误区 5 SSE 流式不需要 Expose-Headers纠正无该配置前端无法读取流式分片 data 字段。
九、本期全文总结1 CORS 是浏览器同源策略配套跨域许可机制,拦截发生在前端而非后端;
2 携带 JSON、鉴权 Token 的 AI 对话全部触发 OPTIONS 预检,必须单独处理;
3 开发可用通配符,生产环境强制填写精准前端域名,禁止*搭配凭证;
4 FastAPI、Nginx、Ollama 三套配置覆盖本地 / 线上 AI 推理全场景;
5 SSE 流式对话需关闭 Nginx 缓冲、暴露自定义响应头;
6 生产推荐 Nginx 统一管控跨域规则,兼顾安全与流式稳定性。 |