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 的继承规则。

笔记加载中…