导读:本期聚焦于刘卫东创作的《Nginx配置中如何通过$scheme变量判断http和https请求?》,敬请观看详情。Nginx配置里判断请求是http还是https,最直接的办法就是借助内置变量$scheme。这个变量会根据实际请求协议返回http或https字符串,可以用在if条件、rewrite规则、日志格式以及反向代理的Header传递等多个场景。本文围绕$scheme的取值原理展开,讲解它和$ssl_protocol、$https变量的区别,演示强制跳转HTTPS的几种常见写法,同时分析if判断的注意事项与性能影响,并给出多server复用配置时统一处理协议判断的实用方案,帮助读者写出更安全、可维护的Nginx配置。

Nginx在处理请求时会把大量请求相关的信息暴露成内置变量,$scheme就是其中使用频率很高的一个。它保存着当前请求使用的协议名称,客户端通过HTTP访问时值为http,通过HTTPS访问时值为https。掌握了这个变量的用法,就可以在同一份配置里完成协议判断、强制跳转、Header透传等常见需求,不必为两种协议分别写两套server块。本文将围绕$scheme的取值机制、典型用法和容易踩的坑展开详细说明。

Nginx配置中如何通过$scheme变量判断http和https请求?

$scheme变量到底是什么,它和$https、$ssl_protocol有什么区别

$scheme是Nginx的内置变量,由core模块提供,取值只有两种可能:http或https。它反映的是客户端与Nginx之间实际建立的连接协议。如果监听端口配置了ssl参数,并且客户端完成了TLS握手,那么$scheme就是https,否则就是http。这个判断是Nginx自动完成的,不需要任何额外配置。

除了$scheme,Nginx还有两个和HTTPS相关的变量容易混淆。一个是$https,它只在连接为HTTPS时返回字符串on,否则为空字符串;另一个是$ssl_protocol,它返回的是具体的TLS协议版本,比如TLSv1.2、TLSv1.3,普通HTTP请求下为空。三者的定位完全不同:$scheme告诉你协议是什么,$https相当于一个布尔开关,$ssl_protocol告诉你TLS版本细节。在做协议判断时,用$scheme最直观,可读性也最好。

还有一个细节需要注意:如果Nginx前面还有CDN或者负载均衡器,客户端到边缘节点是HTTPS,但边缘节点回源到你这台Nginx用的是HTTP,那么$scheme的值就是http,因为变量反映的是与Nginx直连的那一跳。这种场景下应该通过X-Forwarded-Proto请求头来还原真实协议,后面的章节会给出具体写法。

用$scheme实现HTTP强制跳转HTTPS的几种写法

最经典的需求就是把所有HTTP请求301跳转到HTTPS。利用$scheme配合if判断,写法非常清晰:

server {
    listen 80;
    listen 443 ssl;
    server_name example.ipipp.com;

    ssl_certificate     /etc/nginx/ssl/example.crt;
    ssl_certificate_key /etc/nginx/ssl/example.key;

    if ($scheme = http) {
        return 301 https://$host$request_uri;
    }
}

这种写法把80和443端口合并在同一个server块中维护,证书只需要配置一份,跳转逻辑也只有一处,后续要调整域名或证书时不容易漏改。if ($scheme = http)表示当协议是http时触发301重定向,目标地址用$host和$request_uri拼接,保留了原始域名、路径和查询参数,用户访问体验不受影响。

除了if判断,rewrite指令也能配合$scheme完成跳转。不过更简洁的思路是利用rewite内置的条件判断写成三目形式:

server {
    listen 80;
    listen 443 ssl;
    server_name example.ipipp.com;

    ssl_certificate     /etc/nginx/ssl/example.crt;
    ssl_certificate_key /etc/nginx/ssl/example.key;

    # 协议为http时跳转,否则原样处理
    rewrite ^ $scheme://$host$request_uri? permanent;
}

实际项目中更推荐第一种写法。原因是rewrite的正则处理在性能上略逊于直接return,而且rewrite会重新走一遍URI匹配流程,逻辑复杂时排查问题更困难。return指令则直接结束当前处理阶段,语义明确,官方文档也更鼓励用它来做这种简单重定向。另外要注意301和302的选择:301是永久重定向,浏览器会缓存,302是临时重定向。如果跳转规则还在测试阶段,建议先用302,避免浏览器缓存了错误规则导致后续怎么刷新都不生效。

反向代理场景下如何把真实协议传递给后端

Nginx作为反向代理时,后端应用往往需要知道用户原始请求是HTTP还是HTTPS,比如生成跳转链接、设置Cookie的secure属性等。如果直接proxy_pass,后端看到的协议永远是Nginx与后端之间的协议,通常是http,这时就需要借助$scheme把真实协议传过去:

location /api/ {
    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 $scheme;
}

其中X-Forwarded-Proto是事实上的标准头,Spring Boot、Django、Express等主流框架都能识别。Spring Boot只要在配置中开启server.forward-headers-strategy=framework或native,Django只要配置SECURE_PROXY_SSL_HEADER,应用拿到的request.isSecure()就会依据这个头返回正确结果。忘记传这个头是反向代理场景下最常见的问题之一,典型症状是后端生成的链接全部变成http,导致页面出现混合内容警告或者登录后又跳回HTTP。

反过来,如果Nginx前面还有一层负载均衡,真实协议信息在X-Forwarded-Proto头里,Nginx要使用map把头还原到变量中再做判断。map指令只在http块中有效,而且比if更安全,是官方推荐的写法:

http {
    map $http_x_forwarded_proto $real_scheme {
        default $http_x_forwarded_proto;
        ''      $scheme;
    }

    server {
        listen 80;
        server_name example.ipipp.com;

        location / {
            proxy_pass http://backend;
            proxy_set_header X-Forwarded-Proto $real_scheme;
            if ($real_scheme = http) {
                return 301 https://$host$request_uri;
            }
        }
    }
}

这段配置的逻辑是:如果上游传来了X-Forwarded-Proto头,就采用它的值;没有传(说明客户端直连Nginx),就回退到$scheme。这样无论Nginx处于网络架构的哪一层,协议判断都能得到正确结果。

使用$scheme做if判断的注意事项

Nginx的if指令历来被称为evil,因为它只在rewrite阶段执行,且行为在某些上下文中不符合直觉。不过针对$scheme做纯判断并配合return是可以放心使用的,这属于官方文档明确支持的安全用法。需要避免的是在if内部写proxy_pass、rewrite目标含变量这类操作,容易产生意料之外的结果。

另一个常见误区是在日志中排查协议问题时忘记$scheme可以放进log_format。把协议记录下来,事后分析HTTPS覆盖率会方便很多:

log_format main '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $body_bytes_sent '
                'scheme=$scheme ssl_proto=$ssl_protocol';

access_log /var/log/nginx/access.log main;

这样每条访问日志都会带上协议信息,配合awk统计就能快速算出HTTPS流量占比。最后再强调一点:修改任何与$scheme相关的配置后,务必用nginx -t校验语法再reload,跳转规则出错直接影响全站可用性,测试环境先行验证永远是稳妥的做法。

Nginx配置$scheme变量http跳转https修改时间:2026-09-13 20:22:52

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260913/56223.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。