Nginx 跨域 CORS 配置
浏览器"同源策略"规定:页面只能读取同协议、同域名、同端口接口的响应。页面在 a.com 要调 b.com 的 API,必须由 b.com 返回 CORS 响应头声明放行,否则请求发出去了数据也读不到。用 Nginx 反代 API 时,最适合在这里统一处理 CORS。
同源与跨域
两个地址只要协议、域名、端口任一不同即为跨域,例如 http://a.com 的页面请求 http://api.example.com。浏览器会先检查响应头里有没有允许自己的 Origin,没有就拦截数据(请求其实已到达服务器)。
最简单的放行:Access-Control-Allow-Origin
服务端用 Access-Control-Allow-Origin 回应允许的来源:
server {
listen 80;
server_name api.example.com;
add_header Access-Control-Allow-Origin * always; # 允许所有来源
location / {
proxy_pass http://127.0.0.1:8080;
}
}
- 表示不限制来源,适合不带凭证(不携带 Cookie/Authorization)的纯开放接口。always 参数让错误响应也带上该头。
按来源回显:map 动态匹配
需要精确控制可信域名时,用 map 把请求的 Origin 映射成响应头,不在白名单的来源拿不到 CORS 头,浏览器自然拦截:
map $http_origin $cors_origin {
default "";
"~^https?://(www\.)?example\.com$" $http_origin; # 白名单来源原样回显
}
server {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Vary Origin always; # 告诉缓存按 Origin 区分
}
处理预检请求 OPTIONS
带 JSON 请求体、自定义头等"非简单请求"会先发一个 OPTIONS 预检,询问服务端是否允许;需要回应允许的方法与请求头:
server {
location /api/ {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
add_header Access-Control-Max-Age 86400 always; # 预检结果缓存 1 天
return 204; # 预检成功,无内容
}
proxy_pass http://127.0.0.1:8080;
}
}
也可以为 OPTIONS 单独拆 location;实践中常按路径拆分或在 if 里直接 return 204,两种做法等价。
带凭证请求的注意点
前端开启 withCredentials(跨域携带 Cookie)时,Allow-Origin 不能用 *,必须回显具体来源并声明允许凭证:
map $http_origin $cors_origin { ... } # 同上白名单写法
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials true always;
# 注意:Allow-Origin 写 * 与 Allow-Credentials true 同时出现会被浏览器直接拒绝
常见坑
- add_header 不向子级继承:某 location 自己写了 add_header 后,server 层的 CORS 头在该 location 全部失效,需逐级补全。
- 默认只在 2xx/3xx 响应上生效,加 always 参数才能覆盖 4xx/5xx。
- 排查跨域失败先看浏览器 Network 面板响应头是否真的到达,再对照白名单正则是否匹配。
小结
CORS 本质是服务端用响应头声明"允许谁读":无凭证用 *,多可信域用 map 白名单回显,带 Cookie 必须回显具体 Origin 并配 Allow-Credentials true,最后别忘 OPTIONS 预检、always 参数与 add_header 的继承规则。