关键是要分段验证请求链路:先确认Nginx是否收到原始HTTPS协议(查access_log中$scheme),再检查是否正确透传X-Forwarded-Proto头给后端,最后验证后端是否启用框架的转发头解析机制而非硬编码读取协议。

要排查 Nginx 代理转发时后端拿不到真实协议(如 https),关键不是看配置写了没,而是分段验证请求链路上每个环节是否真正透传并被正确识别。
确认 Nginx 是否收到并记录了原始协议
客户端访问的是 https://,Nginx 必须先自己拿到这个信息。在 http 或 server 块中开启详细日志:
- 在
log_format中加入$scheme和$http_x_forwarded_proto,例如:log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$scheme" "$http_x_forwarded_proto"'; - 重启 Nginx 后用浏览器或
curl -kI https://your-domain.com触发一次请求 - 检查 access_log:如果
$scheme是https但$http_x_forwarded_proto为空,说明 Nginx 自己没收到该头——可能是前端有 WAF/SLB 没开启透传,或客户端根本没走 HTTPS
检查 Nginx 是否把协议头正确转发给后端
仅设置 proxy_set_header X-Forwarded-Proto $scheme; 不够,还要确认它确实生效:
- 该指令必须写在
location块内、proxy_pass之后(顺序错误会导致不生效) - 后端服务打印全部请求头(如 Java 的
request.getHeaderNames()、Node.js 的console.log(req.headers)),确认x-forwarded-proto字段存在且值为https - 如果字段缺失,常见原因有:
— Nginx 配置未重载(nginx -t && nginx -s reload)
— 多层代理中上一级覆盖或清空了该头
— 云厂商负载均衡(如阿里云 CLB)默认关闭“透传 X-Forwarded-*”开关,需手动开启
验证后端是否主动读取而非硬编码判断协议
后端框架默认读的是连接层面的协议(即 Nginx 到后端的 HTTP),不是用户原始协议。必须显式切换:
- Spring Boot:
— 配置server.forward-headers-strategy=framework
— 确保 Tomcat 的RemoteIpValve已启用,并设remoteIpHeader="x-forwarded-for"和protocolHeader="x-forwarded-proto" - Express:
— 设置app.set('trust proxy', true),并确保 Nginx 透传了X-Forwarded-Proto和X-Forwarded-For - FastAPI / Flask:
— 使用ProxyFix中间件,指定trusted_hosts或num_proxies - 通用检查:
— 后端代码不能直接调用req.protocol或request.getScheme()
— 应读request.getHeader("X-Forwarded-Proto"),并做非空和安全校验(只接受http/https)
排除多级代理或 CDN 干扰
如果 Nginx 前面还有 CDN、WAF 或云 LB,它们可能过滤或重写协议头:
- 登录对应控制台,查找类似“保留客户端请求头”“透传 X-Forwarded-*”“HTTPS 卸载后传递协议”等开关,确保开启
- 临时绕过 CDN 直连 Nginx 测试:用
curl -H "X-Forwarded-Proto: https" http://nginx-ip/health,再查后端日志是否收到该头 - 若 CDN 强制改写
X-Forwarded-Proto(比如固定写成https),而你同时支持 HTTP 入口,反而会造成误判——此时应让 CDN 只在 HTTPS 入口写该头,或改用更可控的X-Real-Proto自定义头


















