核心是透传X-Forwarded-Proto头并让后端信任该头,否则因$scheme恒为http导致ERR_TOO_MANY_REDIRECTS;需配合Host、X-Real-IP等头及后端框架正确配置。

核心是让后端准确知道用户最初用的是 HTTPS,而不是只看到代理传来的 HTTP 连接。Nginx 默认不传递协议信息,后端(比如 PHP 应用、Django、WordPress)就会误判为不安全连接,反复发起 301 跳转到 HTTPS,形成 ERR_TOO_MANY_REDIRECTS。
必须在 Nginx 中透传 X-Forwarded-Proto 头
SSL 卸载通常发生在 CDN、API 网关(如 APISIX)或前置 Nginx 上,它们解密 HTTPS 后以 HTTP 明文转发给下游 Nginx。此时下游 Nginx 的 $scheme 值永远是 http,不能直接用于判断。
- 如果最外层代理(如 Cloudflare、APISIX)已设置
X-Forwarded-Proto: https,应在 Nginx 配置中优先读取该头:proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; - 若外层可能不传该头(如某些自建网关),建议用
map指令兜底:map $http_x_forwarded_proto $real_scheme {<br> default $http_x_forwarded_proto;<br> "" $scheme;<br>}
再写:proxy_set_header X-Forwarded-Proto $real_scheme;
配套透传 Host 和其他关键头
只传协议还不够。后端生成跳转 URL 时,还需知道用户访问的真实域名、端口和路径,否则会拼出错误地址(如跳到 http://127.0.0.1 或缺参数)。
-
proxy_set_header Host $host;—— 保证后端用用户访问的域名(如example.com),而非上游地址 -
proxy_set_header X-Forwarded-Host $host;和proxy_set_header X-Forwarded-Port $server_port;—— 辅助构建完整 URL -
proxy_set_header X-Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;—— 用于日志与风控
后端必须主动信任并使用这个头
Nginx 传了,后端不认等于白传。不同框架启用方式不同,但目标一致:用 X-Forwarded-Proto 替代默认的 scheme 判断逻辑。
- PHP 应用(如 WordPress、Discuz)需检查
$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https',而非只看$_SERVER['HTTPS'] - Django 需设
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')并开启SECURE_SSL_REDIRECT = True - Next.js(使用 Node.js 服务端)需在中间件中解析该头,并设置
req.protocol = 'https'
避免配置冲突:别让 Nginx 自己也跳 HTTPS
如果下游 Nginx 自身还配了无条件 301 跳转(如宝塔面板“强制 HTTPS”开关生成的 return 301 https://$host$request_uri),而它又收着来自 APISIX 或 CDN 的 HTTP 请求,就会和后端跳转叠加,形成双跳循环。
- 应删除或注释掉这类无条件跳转规则
- 如需保留跳转逻辑,改用带条件的写法:
if ($http_x_forwarded_proto != 'https') {<br> return 301 https://$host$request_uri;<br>} - 特别注意:不要用
$scheme != 'https'做判断,因为卸载后它恒为 http


















