宝塔反向代理需手动配置add_header并加always参数,覆盖所有响应(含错误码),否则浏览器仍拦截跨域请求;必须在location块内添加cors头、显式处理options预检、重载配置并验证生效。

直接说结论:宝塔反向代理本身不自动解决跨域,必须手动补全 add_header 指令并确保生效位置覆盖所有响应(含错误码),否则浏览器仍会拦截请求。
为什么加了反向代理还报 CORS 错误
常见现象是前端发 /api/login 请求后,控制台显示 Access to fetch at 'https://example.com/api/login' from origin 'https://fe.example.com' has been blocked by CORS policy。根本原因不是代理没通,而是 Nginx 代理成功了,但返回的响应里缺关键头,或头没传给 4xx/5xx 响应。
-
add_header没加always参数 → 对 401、500、302 等非 200 响应不生效 - 头写在
location /api/ { ... }里 → OPTIONS 预检请求可能走不到这个块(比如被其他location = /拦截) - 用了
Access-Control-Allow-Origin: *+Access-Control-Allow-Credentials: true→ 浏览器直接拒绝,这是硬性限制 - 前端带
credentials: true,但 Nginx 没透传Origin头 →$http_origin变为空,导致Access-Control-Allow-Origin写成空值
必须加的 add_header 和 always 参数怎么写
进宝塔 → 目标站点 → 设置 → 反向代理 → 编辑对应代理 → 拉到最底部「配置文件」,在 location 块内(不是 server 级)贴入以下内容,顺序不能乱:
add_header 'Access-Control-Allow-Origin' '$http_origin' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
注意点:
-
$http_origin是变量,依赖前端请求带Origin头;若只允单域名,可写死为'https://fe.example.com',但绝不能和'true'同时用* - 必须加
always,否则 401 登录失败、500 后端崩了时,浏览器收不到跨域头,连错误信息都看不到 - 别在别的地方(比如伪静态、主配置文件)重复加这些头,容易冲突
OPTIONS 预检请求必须显式处理
浏览器发预检请求时,如果后端没开 CORS 或挂了,Nginx 默认会把 OPTIONS 转发过去再等响应。但多数后端不处理 OPTIONS,结果超时或返回 404,前端就卡住。
稳妥做法是在同一 location 块里加拦截逻辑:
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
return 204;
}
说明:
- 这段必须和上面的
add_header写在同一location,否则变量作用域失效 - 用
return 204而不是proxy_pass,避免多余转发 - 不要用
try_files或rewrite处理 OPTIONS,Nginx 的if在 location 里虽有限制,但对$request_method是安全的
改完配置后不生效?重载和验证步骤不能跳
宝塔点了「保存」≠ 配置已加载。Nginx 进程还在用旧配置缓存。
- 保存后,**必须点「重载配置」**(不是重启 Nginx,更不是重启服务器)
- 用命令行验证:执行
nginx -t看语法是否通过;再执行nginx -T | grep -A3 'Access-Control'确认头真的出现在最终生效的 location 块里 - 检查是否误改了「默认站点」的配置,而你的域名实际绑定的是另一个站点 —— 宝塔为每个站点生成独立 conf 文件,要进对的站点改
- 浏览器测试前清缓存,或用隐身窗口,避免旧响应头被缓存
最容易被忽略的一点:add_header 的生效范围只限于它所在的 block,且子 block 不会自动继承父 block 的头。哪怕你写在 server 级,只要 location /api/ 里没重复写,它对 /api/ 下的响应也无效。











