大模型跨域配置教程:Nginx 反向代理与 CORS 头设置
浏览器直接调用大模型接口时,如果页面域名和 API 域名不一致,控制台通常会出现 Access-Control-Allow-Origin 相关报错,请求被浏览器拦截。
解决思路不是改前端代码硬绕,而是让 API 和页面同源,或在 API 入口正确返回 CORS 响应头。
下面以 Nginx 反向代理为例,给出可落地的配置步骤。
先判断属于哪种跨域场景
动手前先确认请求链路,不同场景改的地方不一样:
- 前端页面部署在
https://www.example.com,大模型 API 在https://api.example.com或第三方接口,属于跨域请求,需要 API 侧放行来源。 - 页面和 API 本来同域,但通过 Nginx 转发后丢失了响应头,表现为部分接口正常、部分接口报 CORS,优先检查代理是否透传或覆盖了
Access-Control-*头。 - 请求带自定义头(如
Authorization)或Content-Type: application/json,浏览器会先发OPTIONS预检请求,服务端必须正确响应预检,否则正式请求不会发出。
适用条件:你有 Nginx 配置权限,能修改 API 入口的 server 或 location 块;
如果 API 由第三方托管且不允许改响应头,只能改用同源反向代理方案。
Nginx 反向代理加 CORS 头的配置写法
假设页面域名是 https:,
//www.example.com
希望把 /llm-api/ 路径代理到后端大模型服务 http:,
//127.0.0.1:
8000/
让浏览器认为请求同源。
在对应 server 块中加入:
location /llm-api/ {
proxy_pass http://127.0.0.1:8000/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 允许的页面来源,生产环境不要写 *
add_header Access-Control-Allow-Origin "https://www.example.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;
# 预检请求直接返回 204,不再转发给后端
if ($request_method = OPTIONS) {
return 204;
}
}
几个关键点:
always保证即使后端返回 4xx/5xx,CORS 头仍然带上,否则前端只能看到跨域错误、看不到真实状态码。Access-Control-Allow-Origin写具体域名,不要和Access-Control-Allow-Credentials: true同时使用*,浏览器会拒绝。- 如果后端自己也返回了
Access-Control-Allow-Origin,Nginx 的add_header可能产生重复头,需要在代理层用proxy_hide_header Access-Control-Allow-Origin;隐藏后端头后再统一添加。 - 改完执行
nginx -t检查语法,通过后nginx -s reload重载。
验证配置是否真正生效
不要只看浏览器页面,先用命令行确认响应头:
curl -I -X OPTIONS 'https://www.example.com/llm-api/v1/chat/completions' \
-H 'Origin: https://www.example.com' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: authorization,content-type'
预期结果:状态码为 204 或 200,响应头中包含 Access-Control-Allow-Origin: https://www.example.com、Access-Control-Allow-Methods 和 Access-Control-Allow-Headers。
如果缺少某个头,回到配置检查拼写和 always 参数。
再发一次正式请求:
curl -i 'https://www.example.com/llm-api/v1/chat/completions' \
-H 'Origin: https://www.example.com' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 你的令牌' \
-d '{"model":"你的模型名","messages":[{"role":"user","content":"hi"}]}'
观察响应头中是否同样带有允许来源,以及响应体是否正常返回。
浏览器端最后在开发者工具 Network 面板复查,确认没有红色 CORS 提示。
容易踩坑的几个地方
- 只加了正式请求头,没处理 OPTIONS:带
Authorization的请求会先预检,预检失败时正式请求根本不会发出。 - 来源写成通配符又开了凭证:
Access-Control-Allow-Origin: *与Allow-Credentials: true冲突,需要按实际来源动态返回。 - 多层代理头被覆盖:CDN、负载均衡、Nginx 多层转发时,确认最终响应头来自哪一层,避免上游把 CORS 头覆盖掉。
- 路径末尾斜杠不一致:
proxy_pass末尾带/与不带/的路径拼接结果不同,接口 404 时优先核对这一点。 - 改完没重载:配置测试通过但忘记
reload,现象是“改了没效果”。
什么时候不该继续加 CORS 头
如果大模型 API 由第三方提供且明确禁止浏览器直连,正确做法是把调用放到自己的后端服务,由后端转发请求,浏览器只访问同源后端接口。
这样既避开跨域限制,也避免把 API 密钥暴露在前端代码里。
跨域配置本身只是浏览器安全策略的一环,生产环境还应配合来源白名单、请求频率限制和密钥管理一起使用。
配置完成后建议保留一份变更记录,方便后续排查时快速回滚。