要避免 https 重定向循环,核心是让后端准确识别用户原始访问协议,同时确保 nginx 自身跳转逻辑不与后端冲突:必须用两个独立 server 块分离协议处理,http 块仅 return 301 跳转,https 块禁用任何重复跳转指令;proxy_set_header x-forwarded-proto $scheme 动态透传协议,配合 host、x-real-ip 等头,并确保后端框架启用对应代理头信任配置。

要避免 HTTPS 重定向循环,核心是让后端准确识别用户原始访问协议,同时确保 Nginx 自身跳转逻辑不与后端冲突。关键不在“加跳转”,而在“传真实、不硬编码、不重复判”。
只在 HTTP server 块中做 301 跳转
HTTPS server 块里绝不能出现 return 301 https://… 这类指令——它会把已加密的请求又推一遍 HTTPS,直接触发循环。
- ✅ 正确做法:用两个独立 server 块分离协议处理
- ✅ HTTP 块只负责跳转:
server { listen 80; return 301 https://$host$request_uri; } - ❌ 错误写法:在 HTTPS server 中再写一次
return 301 https://...或rewrite ^ https://... permanent
透传 X-Forwarded-Proto 动态值
后端(如 Django、Spring Boot、WordPress)默认只看到 Nginx 发来的 HTTP 请求。若不告诉它“用户其实是走 HTTPS 来的”,它就会反复跳转到 HTTP,形成闭环。
- ✅ 必须在 proxy_pass 所在 location 中加:
proxy_set_header X-Forwarded-Proto $scheme; - ✅ 若前端有 CDN(如 Cloudflare),优先读它传来的头:
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; - ❌ 绝对不要硬编码:
proxy_set_header X-Forwarded-Proto https;——HTTP 入口会失效,本地调试或回源时必出问题
同步传递 Host 和客户端信息
仅传协议还不够。后端生成跳转 URL 时,需要知道用户访问的真实域名、IP 和端口,否则可能拼出 http://localhost:8000 这类无效地址,浏览器跳失败后反复重试,间接导致循环。
-
proxy_set_header Host $host;——确保后端用用户所输域名(如 example.com),而非 upstream 地址 -
proxy_set_header X-Real-IP $remote_addr;和X-Forwarded-For $proxy_add_x_forwarded_for; - 若监听非标准 HTTPS 端口(如 8443),补上:
proxy_set_header X-Forwarded-Port $server_port;
检查并关闭后端的重复跳转逻辑
有些框架(如 Laravel、Rails)默认开启“强制 HTTPS”中间件。当 Nginx 已终止 SSL 并正确透传头后,该中间件若未配置信任代理头,仍会自行跳转,和 Nginx 的跳转形成套娃。
- Django 需设:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') - Spring Boot 推荐:
server.forward-headers-strategy=framework+RemoteIpFilter - WordPress 需在 wp-config.php 中定义:
$_SERVER['HTTPS'] = 'on';或启用FORCE_SSL_ADMIN并配合信任头











