Nginx 反向代理丢失自定义 Header 导致认证失败问题分析

问题描述

在生产环境中,微信小程序登录后无法访问需要认证的 API 接口,返回 401 未登录错误。而同样的代码在本地开发环境运行正常。

现象

  • ✅ 登录接口返回 200,成功获取 token
  • ✅ 前端正确保存 token 到本地存储
  • ✅ 前端在请求头中正确发送 token
  • ❌ 后端收不到 token,返回 401 未登录错误

日志表现

登录检查失败: 未能读取到有效Token
未登录异常: 未能读取到有效Token

根本原因

Nginx 默认会丢弃带下划线的自定义 HTTP Header

在这个案例中:

  • 前端发送的自定义 token header 名称中包含下划线
  • Nginx 反向代理默认配置不转发带下划线的 header
  • 导致后端收不到 token,认证失败

Nginx 的 Header 转发规则

Header 类型默认行为
标准 HTTP Header(如 Host、User-Agent)✅ 自动转发
Authorization Header✅ 自动转发
带下划线的自定义 Header❌ 默认丢弃

调试过程

第一步:确认后端配置正确

  • 对比开发环境和生产环境的 Spring Boot 配置
  • 确认 Sa-Token 框架的 isReadHeader=true 配置已正确加载
  • 后端配置完全一致,问题不在后端

第二步:排查前端代码

  • 验证前端正确保存了 token
  • 验证前端在请求头中正确发送了 token
  • 前端代码也没有问题

第三步:定位 Nginx 问题

在 Nginx 配置中添加调试 header:

location /api/ {
    # ... 其他配置 ...
    
    # 调试:记录接收到的 headers
    add_header X-Debug-Token $http_custom_token always;
    add_header X-Debug-Auth $http_authorization always;
}

通过小程序网络调试工具查看响应头:

  • X-Debug-Auth: Bearer xxx 存在 → Authorization header 被正确转发
  • X-Debug-Token 为空 → 自定义 header 被 Nginx 丢弃

解决方案

方案一:修改 Nginx 配置(推荐)

在 Nginx 的 server 块中添加配置:

server {
    listen 80;
    listen 443 ssl;
    server_name example.com;
    
    # ✅ 允许转发带下划线的 headers
    underscores_in_headers on;
    
    location /api/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # ✅ 显式转发自定义 token header
        proxy_set_header custom_token $http_custom_token;
    }
}

关键点:

  • underscores_in_headers on; 必须放在 server 块内,不能放在 location 块内
  • 在 location 块中使用 proxy_set_header 显式转发需要的 header

方案二:修改前端代码

改用标准的 Authorization header,这样 Nginx 会自动转发:

// 原方式(可能被 Nginx 丢弃)
config.header = {
  'custom_token': token
}

// 改为标准方式(Nginx 自动转发)
config.header = {
  'Authorization': 'Bearer ' + token
}

最佳实践

1. 使用标准 HTTP Header

优先使用标准的 HTTP Header(如 Authorization),避免自定义 header 被代理丢弃的风险。

2. 显式配置代理转发

在反向代理配置中显式指定需要转发的 header:

# 转发所有必要的 headers
proxy_pass_request_headers on;

# 显式转发特定 headers
proxy_set_header Authorization $http_authorization;
proxy_set_header X-Custom-Header $http_x_custom_header;

3. 添加调试 Header

在开发和测试阶段,添加调试 header 帮助排查问题:

add_header X-Debug-Auth $http_authorization always;
add_header X-Debug-Custom $http_custom_token always;

4. 完整的反向代理配置示例

server {
    listen 443 ssl http2;
    server_name api.example.com;
    
    # 允许下划线 headers
    underscores_in_headers on;
    
    location /api/ {
        proxy_pass http://backend:8080;
        
        # 转发必要的 headers
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # 显式转发认证相关 headers
        proxy_set_header Authorization $http_authorization;
        proxy_set_header X-Custom-Auth $http_x_custom_auth;
        
        # 超时配置
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

总结

这个问题的关键在于理解 Nginx 反向代理的 Header 转发机制

  1. 标准 HTTP Header 会自动转发
  2. 带下划线的自定义 Header 默认被丢弃
  3. 需要显式配置才能转发自定义 Header

在设计认证方案时,应该:

  • 优先使用标准的 Authorization header
  • 如果必须使用自定义 header,需要在反向代理中显式配置转发
  • 在生产环境部署前,充分测试代理层的 header 转发

这个问题在微服务架构中很常见,特别是当认证信息通过自定义 header 传递时,一定要注意代理层的配置。