0

Nginx CORS携带自定义Header配置教程:解决跨域请求自定义Header丢失的完整实战

2026.05.23 | youres | 23次围观

为什么自定义Header会触发CORS预检请求

当前端在AJAX请求中设置自定义Header(如X-Requested-WithX-Auth-TokenX-Client-Version)时,浏览器会自动触发CORS预检请求(Preflight Request)。这是因为自定义Header不属于CORS安全列表(Safe List),浏览器必须先询问服务器是否允许该请求。

预检请求使用HTTP OPTIONS方法,携带Access-Control-Request-Headers告诉服务器:实际请求会带上哪些自定义Header。服务器必须在响应中通过Access-Control-Allow-Headers明确批准这些Header,否则浏览器会直接拦截实际请求,控制台报CORS错误。

Nginx处理OPTIONS预检请求的完整配置

预检请求不携带业务数据,服务器只需返回正确的CORS响应头,不需要转发给后端。Nginx可以在location块中直接处理OPTIONS请求:

location /api/ {
    # 处理OPTIONS预检请求
    if ($request_method = 'OPTIONS') {
        add_header Access-Control-Allow-Origin $http_origin;
        add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
        add_header Access-Control-Allow-Headers 'X-Requested-With, X-Auth-Token, Content-Type, Authorization';
        add_header Access-Control-Allow-Credentials 'true';
        add_header Access-Control-Max-Age 86400;
        add_header Content-Type 'text/plain; charset=utf-8';
        return 204;
    }

    # 正常请求的CORS头
    add_header Access-Control-Allow-Origin $http_origin;
    add_header Access-Control-Allow-Credentials 'true';

    # 转发给后端
    proxy_pass http://backend;
}

Access-Control-Max-Age 86400表示预检结果缓存86400秒(24小时),避免每个请求都触发预检,对性能有明显提升。

Access-Control-Allow-Headers配置自定义Header白名单

Access-Control-Allow-Headers的值是一个逗号分隔的Header名称列表,必须与实际请求中Access-Control-Request-Headers的值匹配。常见做法有两种:

方式一:精确列出允许的Header(推荐,安全性更好)

add_header Access-Control-Allow-Headers 'X-Requested-With, X-Auth-Token, X-Client-Version, Content-Type' always;

方式二:使用通配符(方便但安全性较低,且携带credentials时不生效)

add_header Access-Control-Allow-Headers '*' always;

注意:当请求携带Cookie(credentials模式)时,Access-Control-Allow-Headers: *无效,必须明确列出Header名称,且Access-Control-Allow-Origin也不能用*

携带自定义Header时credentials模式的注意事项

当请求需要携带Cookie或HTTP认证信息时,前端需要设置withCredentials: true,此时Nginx配置有三个硬性要求:

  1. Access-Control-Allow-Origin不能是*,必须明确指定origin(可用变量$http_origin动态获取,但需自行校验安全性)
  2. Access-Control-Allow-Credentials必须设为true
  3. Access-Control-Allow-HeadersAccess-Control-Allow-Methods不能用通配符*
# credentials模式的正确配置示例
location /api/ {
    if ($request_method = 'OPTIONS') {
        add_header Access-Control-Allow-Origin $http_origin;
        add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
        add_header Access-Control-Allow-Headers 'X-Auth-Token, Content-Type';
        add_header Access-Control-Allow-Credentials 'true';
        add_header Access-Control-Max-Age 86400;
        return 204;
    }
    add_header Access-Control-Allow-Origin $http_origin always;
    add_header Access-Control-Allow-Credentials 'true' always;
    proxy_pass http://backend;
}

Nginx add_header的always参数与上下文覆盖问题

Nginx的add_header指令有一个容易踩的坑:当某个上下文(如if块或location块)中出现了add_header,它只会生效该上下文中定义的header,上层定义的同名header会被覆盖消失。

解决方法一:在每个add_header后面加always参数,确保无论HTTP状态码是什么都输出该header:

add_header Access-Control-Allow-Origin $http_origin always;

解决方法二:把CORS相关配置抽到独立的include文件中,每个location统一引入,避免重复书写导致覆盖。

常见错误与排查方法

错误1:自定义Header被浏览器拦截,控制台报"not allowed by Access-Control-Allow-Headers"
原因:Access-Control-Allow-Headers未包含该Header名称。解决:在允许列表中追加该Header名称。

错误2:OPTIONS请求返回405 Method Not Allowed
原因:Nginx未单独处理OPTIONS,请求被转发给后端而后端不支持OPTIONS。解决:在location中优先用if ($request_method = 'OPTIONS')拦截返回204。

错误3:credentials模式下仍然用通配符*,浏览器报"credentials mode only supports ..."
原因:credentials模式禁止通配符。解决:将*改为明确的origin和明确的Header列表。

错误4:add_header配置看起来正确,但实际响应中没有CORS头
原因:if块中的add_header覆盖了外层的add_header。解决:统一加always参数,或用include文件统一管理。

使用map指令实现多域名动态CORS白名单

当API需要允许多个指定域名跨域访问时,可以用Nginx的map指令实现动态origin校验,避免直接在配置中硬编码:

http {
    map $http_origin $cors_origin {
        default '';
        '~^https?://localhost(:\d+)?$' $http_origin;
        '~^https?://example\.com$' $http_origin;
        '~^https?://app\.example\.com$' $http_origin;
    }

    server {
        location /api/ {
            if ($request_method = 'OPTIONS') {
                add_header Access-Control-Allow-Origin $cors_origin;
                add_header Access-Control-Allow-Headers 'X-Auth-Token, Content-Type';
                add_header Access-Control-Allow-Credentials 'true';
                add_header Access-Control-Max-Age 86400;
                return 204;
            }
            add_header Access-Control-Allow-Origin $cors_origin always;
            add_header Access-Control-Allow-Credentials 'true' always;
            proxy_pass http://backend;
        }
    }
}

这种方法既支持多域名,又避免了将Access-Control-Allow-Origin设为*的安全风险。


相关文章推荐:
Nginx CORS跨域配置详解:从原理到实战的完整指南
Nginx CORS预检请求OPTIONS处理配置:解决跨域预检失败的完整实战
Nginx CORS与Cookie携带配置教程:解决跨域请求Cookie丢失的完整实战

版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论
881文章数 0评论数
作者其它文章