背景
在一次前后端联调中,前端在请求中新增了一个 Header:
x_log_id: 123456
结果接口直接报错。
经过排查发现:
- 浏览器 Network 中 Header 是存在的
- 后端却 完全接收不到
- Nginx 已配置 CORS:
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
但问题依然存在。
最终,前端将 Header 改为:
x-log-id: 123456
问题立刻解决。
这到底是为什么?
一、问题的根源不是 CORS
一开始很容易误判为 CORS 问题,毕竟是「自定义 Header + 跨域」。
但实际上:
- OPTIONS 预检请求是通过的
- 浏览器也确实把 Header 发出去了
- 问题发生在 Nginx 到后端这一层
二、HTTP Header 命名规范
1️⃣ 标准推荐的写法
HTTP Header 的标准(RFC 7230 / RFC 9110)中:
- Header 名称由 token 构成
历史和实际约定中:
- 推荐使用
-(连字符) - 不推荐使用
_(下划线)
- 推荐使用
常见合法写法:
X-Request-Id
X-Trace-Id
Content-Type
而不是:
x_request_id
x_log_id
三、Nginx 的默认行为(关键点)
1️⃣ Nginx 默认会忽略带 _ 的请求头
Nginx 有一个非常关键但容易被忽略的配置项:
underscores_in_headers off; # 默认值
默认行为是:
- 请求 Header 中如果包含
_ - Nginx 会直接丢弃该 Header
该 Header:
- 在
$http_xxx中取不到 - 不会被转发到后端
- 在
这也是为什么:
浏览器能看到
后端却完全接收不到
四、为什么 CORS 配置解决不了?
你可能已经加了:
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
这个配置的作用是:
- 告诉浏览器:
“我允许你发送这些 Header”
但它 无法影响 Nginx 自身是否接收 Header。
换句话说:
浏览器:我发了
Nginx:我不要
五、为什么改成 x-log-id 就好了?
x-log-id
具备以下特点:
- 使用
-,符合规范 - Nginx 默认支持
- 不需要任何额外配置
- Header 可以完整传递给后端
因此问题立刻消失。
六、可选解决方案(不推荐)
如果你 必须 使用下划线(强烈不推荐),可以在 Nginx 中显式开启:
underscores_in_headers on;
⚠️ 注意:
- 可能带来安全风险
- 与部分代理、网关行为不一致
- 不符合主流 HTTP 规范
七、最佳实践建议
✅ Header 命名规范
- 使用
- - 小写或首字母大写均可(HTTP Header 不区分大小写)
推荐示例:
X-Request-Id
X-Trace-Id
X-Log-Id
❌ 避免:
x_log_id
trace_id
request_id
八、总结
| 问题 | 原因 |
|---|---|
| Header 在浏览器存在 | 浏览器正常发送 |
| 后端收不到 | Nginx 丢弃 _ Header |
| CORS 无法解决 | 问题不在浏览器 |
改成 - 立刻好 | 符合规范 + Nginx 默认支持 |
结论一句话:
这是一个典型的「HTTP 规范 + Nginx 默认行为」导致的坑,而不是前端或 CORS 的锅。