导读:本期聚焦于小白龙创作的《Nginx反向代理中如何正确记录并回源Access-Control-Request-Headers?》,敬请观看详情。为什么Nginx反向代理部署后,浏览器跨域预检请求中的Access-Control-Request-Headers头不是消失就是日志里看不到?这个问题的本质在于预检OPTIONS请求还没有真正进入业务接口,就被Nginx拦截或转发规则改变了。Access-Control-Request-Headers由浏览器自动生成,用于告知服务器后续真实请求会携带哪些自定义头,它是否被正确回源直接影响后端CORS校验能否通过。本文从Nginx日志变量$http_access_control_request_headers入手,说明如何用自定义log_format记录该请求头,再结合proxy_set_header和OPTIONS快速响应策略,给出回源透传的正反两种配置方案。通过curl命令模拟预检请求并观察日志输出,读者可以快速判断当前链路上这个请求头是在哪一环被丢弃,从而修复跨域预检失败问题。

浏览器在执行跨域非简单请求前,会先发出一个 OPTIONS 预检请求。这个预检请求会通过 Access-Control-Request-Headers 头,告诉服务器后续真实请求将携带哪些自定义请求头。Nginx 作为反向代理处在链路最前端,它如何记录这个请求头,以及回源到上游服务时是否完整透传,直接决定了后端跨域校验能不能通过。有些开发者在后端日志里看到 Access-Control-Request-Headers 为空,却不知道问题出在 Nginx 还是更上游。本文从日志变量映射、回源配置、快速响应策略三个层面进行拆解。

Nginx反向代理中如何正确记录并回源Access-Control-Request-Headers?

一、浏览器预检请求与Access-Control-Request-Headers的生成机制

Access-Control-Request-Headers 是 CORS 预检请求的核心头之一。浏览器只会在非简单请求场景自动携带它,比如请求使用了自定义请求头、PUT 或 DELETE 方法,或者 Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data、text/plain 之一。预检 OPTIONS 请求不会直接执行业务逻辑,而是先询问服务器是否允许这些方法和头。服务器必须在响应中返回 Access-Control-Allow-Headers,且内容必须覆盖请求中列出的每一项,否则浏览器会阻断后续真实请求。

在 Nginx 反向代理架构中,预检请求首先进入 Nginx。Nginx 本身的日志默认使用 combined 格式,通常只包含远程地址、时间、请求行、状态码、Referer 和 User-Agent 等字段,并不会展示 Access-Control-Request-Headers。如果开发者只看默认 access.log,就很难判断这个头是否已经到达 Nginx。更关键的是,后面回源到 Node.js、Java、Go 等上游服务时,如果 Nginx 在 location 块中直接拦截 OPTIONS 请求,预检根本不会进入业务应用,后端日志当然看不到任何内容。理解这些机制是排查跨域预检失败的前提。

二、Nginx 日志变量映射与自定义 log_format

Nginx 为请求头提供了统一的变量映射规则:先把请求头名称中的连字符替换成下划线,再全部转为小写,最后加上 http_ 前缀。于是 Access-Control-Request-Headers 对应的变量就是 $http_access_control_request_headers。这个变量在请求头不存在时值为空字符串,在日志中通常显示为 -。同理,Access-Control-Request-Method 对应 $http_access_control_request_method 变量。

要在日志中记录该头,需要自定义 log_format。配置可以放在 http 块中,然后在 access_log 指令中引用。以下配置增加了一个 cors_debug 格式,方便同时观察预检方法和请求头:

http {
    log_format cors_debug '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" '
                          'preflight_method=$http_access_control_request_method '
                          'preflight_headers=$http_access_control_request_headers';

    access_log /var/log/nginx/access.log cors_debug;
}

配置完成后,执行 nginx -s reload 重新加载。当浏览器发起预检时,日志每行末尾就会出现 preflight_method 和 preflight_headers 字段。如果 preflight_headers 有值,说明 Nginx 确实收到了该请求头;如果一直是 -,则需要检查前端是否真的发出预检,或者请求是否被更上层代理拦截。这种日志方案不改变转发行为,对生产环境影响很小,适合临时排查或长期开启。

三、回源透传与 OPTIONS 快速响应策略

在默认配置下,Nginx 会把原始请求头转发给上游服务,包括 Access-Control-Request-Headers。如果没有显式覆盖,回源链路通常不会丢失这个头。但实际项目中,很多团队会在 location 块里直接拦下 OPTIONS 请求,统一返回 204 或 200,这样就避免了预检请求打到后端。这种方案可以减少上游压力,但要求 Nginx 必须返回正确的 CORS 响应头,否则会引发布置了多个服务的跨域配置不一致。

以下示例展示了由 Nginx 直接响应预检的做法,其中 Access-Control-Allow-Headers 的值直接取自请求中的 $http_access_control_request_headers

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 $http_access_control_request_headers always;
        add_header Access-Control-Max-Age 86400 always;
        return 204;
    }

    proxy_pass http://backend_upstream;
}

如果仍然希望把预检请求回源到后端,由后端框架处理 CORS,那么一般不需要额外设置,默认透传即可。但有些安全加固配置会通过 proxy_set_header 覆盖或清理请求头,这时可以显式把该头传给上游:

location /api/ {
    proxy_pass http://backend_upstream;
    proxy_set_header Access-Control-Request-Headers $http_access_control_request_headers;
    proxy_set_header Access-Control-Request-Method  $http_access_control_request_method;
}

需要注意,如果变量为空,这种写法会让上游收到一个空值的请求头。是否可接受取决于后端框架的容错能力。更稳妥的做法是保持默认转发,并通过日志确认该头在到达 Nginx 时就是完整的。只有当上游明确反馈收不到该头时,才需要显式设置。回源配置的核心不是无脑加 proxy_set_header,而是先通过日志定位请求头在哪个环节消失。

四、用 curl 验证预检请求的日志与回源结果

curl 可以完全模拟浏览器的预检请求,很适合在修改 Nginx 配置后快速验证。下面命令中的反斜杠用于 bash 换行,执行时保留即可:

curl -I -X OPTIONS http://ipipp.com/api/users 
  -H "Origin: https://app.ipipp.com" 
  -H "Access-Control-Request-Method: POST" 
  -H "Access-Control-Request-Headers: X-Custom-Token, Content-Type"

命令执行后,可以打开 Nginx 的 access.log 查看自定义字段。如果看到 preflight_headers=X-Custom-Token, Content-Type,说明日志配置生效且请求头已到达 Nginx。接着观察响应头,若返回了 Access-Control-Allow-Origin 和 Access-Control-Allow-Headers,并且内容与请求匹配,就代表 CORS 预检通过。若响应头缺失或值不完整,需要回头检查 add_header 是否加了 always,因为非 200 状态码下,部分响应头默认不会被发送。

排查过程中还要注意浏览器缓存。Access-Control-Max-Age 会让浏览器把预检结果缓存一段时间,如果修改配置后没有重新验证,可以先在浏览器开发者工具里勾选禁用缓存,或者换一个带新 Origin 的请求再测。另外,如果在 Nginx 前方还有 CDN 或负载均衡器,也需要确认它们是否把 Access-Control-Request-Headers 正确传递到 Nginx。日志方案可以把每一层是否收到该头变成可观测的事实,避免凭感觉猜测。

Nginx日志Access-Control-Request-Headers反向代理回源修改时间:2026-08-20 00:09:34

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。