如何用Nginx proxy_set_header和$scheme实现HTTPS路径长尾词改写?
- 内容介绍
- 文章标签
- 相关推荐
本文共计715个文字,预计阅读时间需要3分钟。
由于+Nginx+与后端通信默认走+HTTP+(例如+proxy_pass http://127.0.0.1:3000+),后端看到的请求协议就是+http+。它不知道用户实际使用的是+HTTPS+。一旦后端接收到跳转地址、API、基础路径或静态资源链接,就会硬编码这些链接为+HTTP+。
proxy_set_header X-Forwarded-Proto $scheme 必须加在 location 块里
这行配置不能写在 server 或 http 块顶层,必须紧贴 proxy_pass 前面,且位于具体 location 内。否则变量 $scheme 可能为空或取值错误。
-
$scheme是 Nginx 内置变量,值为http或https,由当前请求监听的端口决定(listen 443 ssl→https) - 不要写成
proxy_set_header X-Forwarded-Proto https——HTTP 请求也会被标记为 HTTPS,引发重定向循环 - 如果 Nginx 前还有 CDN(如 Cloudflare),要先确认它是否已设
X-Forwarded-Proto;若已设,应改用$http_x_forwarded_proto透传,而非覆盖
后端不信任 X-Forwarded-Proto 就等于白配
只在 Nginx 里加 proxy_set_header 不够,后端框架默认忽略这个头,防止伪造。必须显式开启代理信任,并限定可信来源 IP(通常是 Nginx 所在机器):
- Django:设置
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https'),并确保USE_X_FORWARDED_HOST = True - Flask:用
ProxyFix,例如app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1),且只信任内网 IP - Spring Boot:设
server.forward-headers-strategy=framework,配合server.tomcat.protocol-header=x-forwarded-proto - Node.js(Express):
app.set('trust proxy', '127.0.0.1'),然后req.protocol才会返回https
验证是否真正生效的三个关键点
别只看 Nginx 配置有没有写,要从请求链路末端反推:
- 在后端加一行日志,打印原始 header:
console.log(req.headers['x-forwarded-proto'])(Node)或request.META.get('HTTP_X_FORWARDED_PROTO')(Django)——HTTPS 访问时必须输出https - 检查后端生成的重定向响应头:
curl -I https://yoursite.com/login,看Location是否为https://...而非http://... - 打开浏览器开发者工具 → Network → 点一个 API 请求 → 查看请求头中
X-Forwarded-Proto是否存在且值正确;再看响应体里的绝对 URL 是否全为 HTTPS
最容易被忽略的是后端信任配置中的 IP 范围——填 0.0.0.0/0 有安全风险,漏掉 IPv6 地址(如 ::1)会导致部分请求协议识别失败。
本文共计715个文字,预计阅读时间需要3分钟。
由于+Nginx+与后端通信默认走+HTTP+(例如+proxy_pass http://127.0.0.1:3000+),后端看到的请求协议就是+http+。它不知道用户实际使用的是+HTTPS+。一旦后端接收到跳转地址、API、基础路径或静态资源链接,就会硬编码这些链接为+HTTP+。
proxy_set_header X-Forwarded-Proto $scheme 必须加在 location 块里
这行配置不能写在 server 或 http 块顶层,必须紧贴 proxy_pass 前面,且位于具体 location 内。否则变量 $scheme 可能为空或取值错误。
-
$scheme是 Nginx 内置变量,值为http或https,由当前请求监听的端口决定(listen 443 ssl→https) - 不要写成
proxy_set_header X-Forwarded-Proto https——HTTP 请求也会被标记为 HTTPS,引发重定向循环 - 如果 Nginx 前还有 CDN(如 Cloudflare),要先确认它是否已设
X-Forwarded-Proto;若已设,应改用$http_x_forwarded_proto透传,而非覆盖
后端不信任 X-Forwarded-Proto 就等于白配
只在 Nginx 里加 proxy_set_header 不够,后端框架默认忽略这个头,防止伪造。必须显式开启代理信任,并限定可信来源 IP(通常是 Nginx 所在机器):
- Django:设置
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https'),并确保USE_X_FORWARDED_HOST = True - Flask:用
ProxyFix,例如app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1),且只信任内网 IP - Spring Boot:设
server.forward-headers-strategy=framework,配合server.tomcat.protocol-header=x-forwarded-proto - Node.js(Express):
app.set('trust proxy', '127.0.0.1'),然后req.protocol才会返回https
验证是否真正生效的三个关键点
别只看 Nginx 配置有没有写,要从请求链路末端反推:
- 在后端加一行日志,打印原始 header:
console.log(req.headers['x-forwarded-proto'])(Node)或request.META.get('HTTP_X_FORWARDED_PROTO')(Django)——HTTPS 访问时必须输出https - 检查后端生成的重定向响应头:
curl -I https://yoursite.com/login,看Location是否为https://...而非http://... - 打开浏览器开发者工具 → Network → 点一个 API 请求 → 查看请求头中
X-Forwarded-Proto是否存在且值正确;再看响应体里的绝对 URL 是否全为 HTTPS
最容易被忽略的是后端信任配置中的 IP 范围——填 0.0.0.0/0 有安全风险,漏掉 IPv6 地址(如 ::1)会导致部分请求协议识别失败。

