如何用Nginx proxy_set_header和$scheme改写HTTPS路径为长尾?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1003个文字,预计阅读时间需要5分钟。
直接说结论:
为什么 $scheme 不等于客户端实际访问协议?
Nginx 默认的 $scheme 变量取自当前请求解析后的协议(即 Nginx 自己“看到”的协议),不是客户端原始请求协议。当你的服务前有 CDN、负载均衡器或四层代理(如 AWS ALB、腾讯云 CLB),且它们以 HTTP 协议转发到 Nginx 时,Nginx 收到的就是 http,$scheme 就是 http——哪怕用户浏览器地址栏显示的是 https://。
解决办法是:依赖 X-Forwarded-Proto 请求头,并在 Nginx 中显式重写 $scheme:
map $http_x_forwarded_proto $real_scheme { default $scheme; https https; }
然后在 location 块中使用 $real_scheme 替代原生 $scheme:
立即学习“前端免费学习笔记(深入)”;
- 确保上游代理(CDN/ALB)确实设置了
X-Forwarded-Proto: https - 不要在
map外直接用set $real_scheme $http_x_forwarded_proto——set在 rewrite 阶段执行太晚,map是变量预解析,更可靠 - 如果没配
map,直接用$scheme会导致后端误判,生成http://链接,触发混合内容警告
proxy_set_header X-Forwarded-Proto $real_scheme 必须加吗?
必须加,而且要加在 proxy_pass 所在的 location 块内(不是 server 块顶层)。这是后端服务(如 Node.js、Django、Spring Boot)判断客户端真实协议的主要依据。
常见错误配置:
server { listen 80; location / { proxy_pass http://backend; # ❌ 错误:这里没传 X-Forwarded-Proto,后端永远读不到 https } }
正确写法:
location / { proxy_pass http://backend; 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 $real_scheme; # ✅ 关键一行 }
- 后端框架(如 Express 的
app.set('trust proxy', true))依赖这个 header 决定是否生成https://URL - 前端若通过 API 获取资源路径(比如上传回调地址、WebSocket 地址),后端必须基于
X-Forwarded-Proto构建,否则返回http://导致连接被浏览器拦截 - 不加这行,
$scheme对前端构建时的运行时无意义——它只影响后端逻辑和响应头,不影响 HTML 中的硬编码路径
前端静态资源路径怎么“动态化”?别改代码,改构建或 Nginx
所谓“全站 HTTPS 路径硬编码”,通常指前端打包后 HTML 中的 <script src="/js/app.js"> 看似没问题,但一旦页面被 https:// 加载,而某个接口或图片仍用 http:// 绝对路径(比如 <img src="http://cdn.example.com/logo.png">),就会触发混合内容阻断。
真正要动的是资源引用方式:
- 构建时用相对路径或协议相对路径:
//cdn.example.com/logo.png(自动继承当前页面协议) - 构建工具(如 Webpack/Vite)配置
base: "/"或build.assetsDir,避免生成http://绝对 URL - 如果必须用 Nginx 注入(比如 legacy 项目无法改构建),可在
location /index.html中用sub_filter替换:location = /index.html { sub_filter 'http://' 'https://'; sub_filter_once off; proxy_pass http://backend; }但注意:这会破坏缓存、增加 CPU 开销,仅作兜底
最容易被忽略的一点:Nginx 的 map 块必须放在 http 块顶层,不能嵌套在 server 或 location 里;否则变量未定义,$real_scheme 为空,X-Forwarded-Proto 会被设成空字符串,后端反而更难处理。
本文共计1003个文字,预计阅读时间需要5分钟。
直接说结论:
为什么 $scheme 不等于客户端实际访问协议?
Nginx 默认的 $scheme 变量取自当前请求解析后的协议(即 Nginx 自己“看到”的协议),不是客户端原始请求协议。当你的服务前有 CDN、负载均衡器或四层代理(如 AWS ALB、腾讯云 CLB),且它们以 HTTP 协议转发到 Nginx 时,Nginx 收到的就是 http,$scheme 就是 http——哪怕用户浏览器地址栏显示的是 https://。
解决办法是:依赖 X-Forwarded-Proto 请求头,并在 Nginx 中显式重写 $scheme:
map $http_x_forwarded_proto $real_scheme { default $scheme; https https; }
然后在 location 块中使用 $real_scheme 替代原生 $scheme:
立即学习“前端免费学习笔记(深入)”;
- 确保上游代理(CDN/ALB)确实设置了
X-Forwarded-Proto: https - 不要在
map外直接用set $real_scheme $http_x_forwarded_proto——set在 rewrite 阶段执行太晚,map是变量预解析,更可靠 - 如果没配
map,直接用$scheme会导致后端误判,生成http://链接,触发混合内容警告
proxy_set_header X-Forwarded-Proto $real_scheme 必须加吗?
必须加,而且要加在 proxy_pass 所在的 location 块内(不是 server 块顶层)。这是后端服务(如 Node.js、Django、Spring Boot)判断客户端真实协议的主要依据。
常见错误配置:
server { listen 80; location / { proxy_pass http://backend; # ❌ 错误:这里没传 X-Forwarded-Proto,后端永远读不到 https } }
正确写法:
location / { proxy_pass http://backend; 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 $real_scheme; # ✅ 关键一行 }
- 后端框架(如 Express 的
app.set('trust proxy', true))依赖这个 header 决定是否生成https://URL - 前端若通过 API 获取资源路径(比如上传回调地址、WebSocket 地址),后端必须基于
X-Forwarded-Proto构建,否则返回http://导致连接被浏览器拦截 - 不加这行,
$scheme对前端构建时的运行时无意义——它只影响后端逻辑和响应头,不影响 HTML 中的硬编码路径
前端静态资源路径怎么“动态化”?别改代码,改构建或 Nginx
所谓“全站 HTTPS 路径硬编码”,通常指前端打包后 HTML 中的 <script src="/js/app.js"> 看似没问题,但一旦页面被 https:// 加载,而某个接口或图片仍用 http:// 绝对路径(比如 <img src="http://cdn.example.com/logo.png">),就会触发混合内容阻断。
真正要动的是资源引用方式:
- 构建时用相对路径或协议相对路径:
//cdn.example.com/logo.png(自动继承当前页面协议) - 构建工具(如 Webpack/Vite)配置
base: "/"或build.assetsDir,避免生成http://绝对 URL - 如果必须用 Nginx 注入(比如 legacy 项目无法改构建),可在
location /index.html中用sub_filter替换:location = /index.html { sub_filter 'http://' 'https://'; sub_filter_once off; proxy_pass http://backend; }但注意:这会破坏缓存、增加 CPU 开销,仅作兜底
最容易被忽略的一点:Nginx 的 map 块必须放在 http 块顶层,不能嵌套在 server 或 location 里;否则变量未定义,$real_scheme 为空,X-Forwarded-Proto 会被设成空字符串,后端反而更难处理。

