【问题标题】:How to find out if you're using HTTPS without $_SERVER['HTTPS']如何确定您是否在没有 $_SERVER['HTTPS'] 的情况下使用 HTTPS
【发布时间】:2010-11-13 14:09:57
【问题描述】:

我在网上看到很多教程说你需要检查$_SERVER['HTTPS'] 如果服务器的连接是用 HTTPS 保护的。我的问题是,在我使用的某些服务器上,$_SERVER['HTTPS'] 是一个未定义的变量,会导致错误。是否有另一个我可以检查的变量应该始终被定义?

为了清楚起见,我目前正在使用此代码来解析是否是 HTTPS 连接:

if(isset($_SERVER['HTTPS'])) {
    if ($_SERVER['HTTPS'] == "on") {
        $secure_connection = true;
    }
}

【问题讨论】:

  • 万一那些 $_SERVER['HTTPS'] 未定义的服务器在 HTTPS 上运行?
  • 其实其中之一就是我家的 WAMP 服务器。而且我不相信它是在 HTTPS 上运行的。
  • @TylerCarter, An alternative method is to use Secure cookies. 不过要小心陷阱。

标签: php https


【解决方案1】:

您可以检查$_SERVER['SERVER_PORT'],因为 SSL 通常在端口 443 上运行,但这并非万无一失。

【讨论】:

  • $_SERVER['SERVER_PORT'] 可以。
【解决方案2】:

Chacha,根据 PHP 文档:“如果脚本是通过 HTTPS 协议查询的,则设置为非空值。”因此,在 HTTPS 确实打开的许多情况下,您的 if 语句将返回 false。您需要验证 $_SERVER['HTTPS'] 存在且非空。如果给定服务器的 HTTPS 设置不正确,您可以尝试检查 $_SERVER['SERVER_PORT'] == 443 是否正确。

但请注意,某些服务器也会将$_SERVER['HTTPS'] 设置为非空值,因此请务必检查此变量。

参考:Documentation for $_SERVER and $HTTP_SERVER_VARS [deprecated]

【讨论】:

  • 使用 $_SERVER['SERVER_PORT'] 可能会很棘手...例如 ispconfig 使用端口 81 作为安全端口,因此可以说 443 是 ssl 的“默认”端口。
  • @Gabriel Sosa - 是的,但可以根据具体情况解决警告。 @hobodave 的答案适用于大多数人。
  • 请注意,这在反向代理后面不起作用。也可以考虑查看HTTP_X_FORWARDED_PROTO 或HTTP_X_FORWARDED_SSL。
  • 我同意最后的手段应该是端口号,所以这是我的检查:(((isset($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) == 'on')) || ((isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'))),它根本不包括端口检查。随意添加。 :-)
  • @paolo 在反向代理 SetEnvIf X-Forwarded-SSL on HTTPS=on 后面会成功。但这不适用于REQUEST_SCHEME,因为 php 似乎更适合使用$_SERVER['HTTPS']
【解决方案3】:

根据 hobodave 的帖子:“如果脚本是通过 HTTPS 协议查询的,则设置为非空值。”

if (!empty($_SERVER['HTTPS']))
{
    $secure_connection = true;
}

【讨论】:

  • 让它(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
【解决方案4】:

即使$_SERVER['HTTPS'] 未定义,这也应该始终有效:

function isSecure() {
  return
    (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
    || $_SERVER['SERVER_PORT'] == 443;
}

代码与 IIS 兼容。

来自PHP.net documentation and user comments:

  1. 如果脚本是通过 HTTPS 协议查询的,则设置为非空值。

  2. 请注意,在将 ISAPI 与 IIS 一起使用时,如果请求不是通过 HTTPS 协议发出的,则该值将为“off”。 (对于将 PHP 作为 Fast-CGI 应用程序运行的 IIS7 报告了相同的行为)。

此外,即使安全连接,Apache 1.x 服务器(和损坏的安装)也可能没有定义$_SERVER['HTTPS']。虽然不能保证,但 convention 上的端口 443 上的连接可能使用 secure sockets,因此需要额外的端口检查。

补充说明:如果客户端和你的服务器之间有负载均衡器,这段代码不会测试客户端和负载均衡器之间的连接,而是测试负载均衡器和你的服务器之间的连接。要测试前一个连接,您必须使用 HTTP_X_FORWARDED_PROTO 标头进行测试,但这样做要复杂得多;请参阅此答案下方的latest comments。

【讨论】:

  • 注:端口 443 不保证连接是加密的
  • @DavidRodrigues 这不是真的。您可以在任何您想要的端口上使用 HTTP/HTTPS。 getservbyname() 只是一个参考,不是现实,并且不以任何方式保证 HTTPS 在端口 443 上运行。
  • 我对@9​​87654330@ 有一个小问题,我不得不将$_SERVER['SERVER_PORT] 转换为一个整数,如下所示:intval($_SERVER['SERVER_PORT]) !== 443
  • 1) 服务器端口检查是 sheetty 服务器的一项额外功能,如果不需要,最好将其删除。 2)请注意,我的回答是一个松散的比较;)
  • 我今天遇到的另一个小问题。服务器返回 'OFF' 而不是 'off' - strtolower($_SERVER['HTTPS']) !== 'off' 成功了。
【解决方案5】:

真正的答案:准备复制粘贴到 [config] 脚本中

/* configuration settings; X=edit may 10th '11 */
$pv_sslport=443; /* for it might be different, as also Gabriel Sosa stated */
$pv_serverport=80; /* X */
$pv_servername="mysite.com"; /* X */

/* X appended after correction by Michael Kopinsky */
if(!isset($_SERVER["SERVER_NAME"]) || !$_SERVER["SERVER_NAME"]) {
    if(!isset($_ENV["SERVER_NAME"])) {
        getenv("SERVER_NAME");
        // Set to env server_name
        $_SERVER["SERVER_NAME"]=$_ENV["SERVER_NAME"];
    }
}
if(!$_SERVER["SERVER_NAME"]) (
    /* X server name still empty? ... you might set $_SERVER["SERVER_NAME"]=$pv_servername; */
}

if(!isset($_SERVER["SERVER_PORT"]) || !$_SERVER["SERVER_PORT"]) {
    if(!isset($_ENV["SERVER_PORT"])) {
        getenv("SERVER_PORT");
        $_SERVER["SERVER_PORT"]=$_ENV["SERVER_PORT"];
    }
}
if(!$_SERVER["SERVER_PORT"]) (
    /* X server port still empty? ... you might set $_SERVER["SERVER_PORT"]=$pv_serverport; */
}

$pv_URIprotocol = isset($_SERVER["HTTPS"]) ? (($_SERVER["HTTPS"]==="on" || $_SERVER["HTTPS"]===1 || $_SERVER["SERVER_PORT"]===$pv_sslport) ? "https://" : "http://") :  (($_SERVER["SERVER_PORT"]===$pv_sslport) ? "https://" : "http://");

$pv_URIprotocol 现在是正确的并且可以使用了;例如$site=$pv_URIprotocol.$_SERVER["SERVER_NAME"]。当然,字符串也可以替换为 TRUE 和 FALSE。 PV 代表 PortalPress 变量,因为它是一种直接复制粘贴,始终有效。这件作品可以在生产脚本中使用。

【讨论】:

    【解决方案6】:

    我刚刚遇到了一个问题,我使用 Apache mod_ssl 运行服务器,但是 phpinfo() 和 var_dump( $_SERVER ) 显示 PHP 仍然认为我在端口 80 上。

    这是我为遇到相同问题的人提供的解决方法....

    <VirtualHost *:443>
      SetEnv HTTPS on
      DocumentRoot /var/www/vhost/scratch/content
      ServerName scratch.example.com
    </VirtualHost>
    

    值得注意的行是 SetEnv 行。有了这个并在重新启动后,您应该拥有您梦寐以求的 HTTPS 环境变量

    【讨论】:

    • 最好确保 HTTPS 真正有效;如果不是,那会让服务器对你撒谎。
    • 您还需要 SetEnv 模块才能使其工作。它默认启用,但您永远不知道服务器管理员可能会禁用什么。
    • 如果您通过反向代理在 docker 上,这非常有用。谢谢!
    【解决方案7】:

    我不认为添加端口是个好主意——尤其是当您拥有许多具有不同构建的服务器时。这只是增加了一件要记住要改变的事情。看文档,我认为凯撒的最后一行很好,所以:

    if(!empty($_SERVER["HTTPS"]))
      if($_SERVER["HTTPS"]!=="off")
        return 1; //https
      else
        return 0; //http
    else
      return 0; //http
    

    似乎已经足够完美了。

    【讨论】:

      【解决方案8】:

      唯一可靠的方法是 Igor M 描述的方法。

      $pv_URIprotocol = isset($_SERVER["HTTPS"]) ? (($_SERVER["HTTPS"]==="on" || $_SERVER["HTTPS"]===1 || $_SERVER["SERVER_PORT"]===$pv_sslport) ? "https://" : "http://") :  (($_SERVER["SERVER_PORT"]===$pv_sslport) ? "https://" : "http://");
      

      考虑以下内容: 您正在使用带有 fastcgi 的 nginx,默认情况下(debian,ubuntu)fastgi_params 包含指令:

      fastcgi_param HTTPS $https;

      如果您不使用 SSL,它会被翻译为空值,而不是“关闭”,而不是 0 而你注定要失败。

      http://unpec.blogspot.cz/2013/01/nette-nginx-php-fpm-redirect.html

      【讨论】:

        【解决方案9】:

        我的解决方案(因为标准条件 [$_SERVER['HTTPS'] == 'on'] 在负载均衡器后面的服务器上不起作用)是:

        $isSecure = false;
        if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
            $isSecure = true;
        }
        elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') {
            $isSecure = true;
        }
        $REQUEST_PROTOCOL = $isSecure ? 'https' : 'http';
        

        HTTP_X_FORWARDED_PROTO:一个事实上的标准,用于识别 HTTP 请求的原始协议,因为反向代理(负载平衡器)可以使用 HTTP 与 Web 服务器通信,即使是对反向代理的请求是 HTTPS http://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Common_non-standard_request_headers

        【讨论】:

        • 如果你使用 varnish 反向代理,这就是解决方案。
        • 这个解决方案解决了我的问题。 (PHP - AWS Elastic beanstalk)
        • 这也适用于 CloudFlare 提供的免费 HTTPS。
        • 值得考虑的是,从安全的角度来看,HTTP_X_FORWARDED_PROTO 代理解决方法可能允许聪明的攻击者通过说他们使用 SSL 来避免 SSL。
        • 这应该被标记为正确答案。
        【解决方案10】:

        我发现这些参数也可以接受,而且在切换 Web 服务器时更可能不会出现误报。

        1. $_SERVER['HTTPS_KEYSIZE']
        2. $_SERVER['HTTPS_SECRETKEYSIZE']
        3. $_SERVER['HTTPS_SERVER_ISSUER']
        4. $_SERVER['HTTPS_SERVER_SUBJECT']

          if($_SERVER['HTTPS_KEYSIZE'] != NULL){/*do foobar*/}
          

        【讨论】:

        • 这并没有告诉你任何关于负载均衡器/代理的 HTTPS 使用情况。
        【解决方案11】:

        我使用的最短方法:

        $secure_connection = !empty($_SERVER['HTTPS']);
        

        如果使用 https,则 $secure_connection 为真。

        【讨论】:

        • echo (!empty($_SERVER['HTTPS'])?'https':'http'); 给你http 或https
        • ,让它(!empty($_SERVER['HTTPS']) &amp;&amp; $_SERVER['HTTPS'] !== 'off')
        【解决方案12】:

        这是一个我已经使用了一段时间的可重用函数。 HTH。

        注意:HTTPS_PORT 的值(这是我代码中的自定义常量)可能因您的环境而异,例如它可能是 443 或 81。

        /**
         * Determine if this is a secure HTTPS connection
         * 
         * @return  bool    True if it is a secure HTTPS connection, otherwise false.
         */
        function isSSL()
        {
            if (isset($_SERVER['HTTPS'])) {
                if ($_SERVER['HTTPS'] == 1) {
                    return true;
                } elseif ($_SERVER['HTTPS'] == 'on') {
                    return true;
                }
            } elseif ($_SERVER['SERVER_PORT'] == HTTPS_PORT) {
                return true;
            }
        
            return false;
        }
        

        【讨论】:

          【解决方案13】:

          你怎么看?

          if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
              $scheme = 'https';
          else
              $scheme = 'http';
          

          【讨论】:

          • 是的。如果你只依赖 empty() 如果没有 'HTTPS' 索引,PHP 将退出并报错。
          • "empty() 本质上等同于 !isset($var) || $var == false" - php.net/manual/en/function.empty.php
          • 你是对的。有趣的是我错过了那个。我一直认为如果变量不存在,empty() 会失败。
          【解决方案14】:

          在我的服务器(Ubuntu 14.10、Apache 2.4、php 5.5)上,当通过 https 加载 php 脚本时,变量 $_SERVER['HTTPS'] 未设置。我不知道出了什么问题。但是.htaccess 文件中的以下行修复了这个问题:

          RewriteEngine on
          
          RewriteCond %{HTTPS} =on [NC] 
          RewriteRule .* - [E=HTTPS:on,NE]
          

          【讨论】:

            【解决方案15】:

            只是为了兴趣,chrome canary 此刻发送

            HTTPS : 1
            

            到服务器,并且取决于服务器的配置方式,可能意味着您会返回以下内容

            HTTPS : 1, on
            

            这破坏了我们的应用程序,因为我们正在测试是否开启,但显然不是。 目前,似乎只有 chrome canary 可以做到这一点,但值得注意的是,canary 中的东西通常会在不久后进入“正常”的 chrome。

            【讨论】:

              【解决方案16】:

              如果您使用 nginx 作为负载平衡系统检查 $_SERVER['HTTP_HTTPS'] == 1 其他检查将失败 ssl。

              【讨论】:

                【解决方案17】:

                这也适用于$_SERVER['HTTPS'] 未定义

                if( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443 ){
                    //enable secure connection
                }
                

                【讨论】:

                • 它们是一些服务器,其中$_SERVER['HTTPS'] 未定义但 https 已启用。那怎么样?
                • @JohnMax SERVER_PORT 总是被定义来解决HTTPS 的未定义问题
                【解决方案18】:

                如果您使用的是 Apache,您可能总是可以信赖

                $_SERVER["REQUEST_SCHEME"]
                

                验证请求的 URL 的方案。但是,正如其他答案中提到的,在假设 SSL 确实被使用之前验证其他参数是谨慎的。

                【讨论】:

                • 它适用于 XAMPP 但不适用于 centos / apache2 + PHP ......所以它不被信任。
                【解决方案19】:

                如果您使用 Incapsula 的负载均衡器,则需要使用 IRule 为您的服务器生成自定义标头。我创建了一个 HTTP_X_FORWARDED_PROTO 标头,如果端口设置为 80,则该标头等于“http”,如果端口设置为 443,则该标头等于“https”。

                【讨论】:

                  【解决方案20】:
                  $secure_connection = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || (!empty($_SERVER['HTTP_HTTPS']) && $_SERVER['HTTP_HTTPS'] != 'off') || $_SERVER['REQUEST_SCHEME'] == 'https' || $_SERVER['SERVER_PORT'] == 443) ? true : false;
                  

                  代码正在检查任何可能的内容,并且也可以在 IIS Web 服务器上运行。自 v44 以来的 Chrome 不设置标头 HTTP: 1 所以检查 HTTP_HTTPS 是可以的。如果此代码与 https 不匹配,则意味着您的网络服务器或代理服务器配置不当。 Apache 本身正确设置了 HTTPS 标志,但是当您使用代理(例如 nginx)时可能会出现问题。您必须在 nginx https 虚拟主机中设置一些标头

                  proxy_set_header   X-HTTPS 1;
                  

                  并使用一些 Apache 模块通过从代理中查找 X-HTTPS 来正确设置 HTTPS 标志。搜索 mod_fakessl、mod_rpaf 等。

                  【讨论】:

                    【解决方案21】:

                    我会添加一个全局过滤器以确保我检查的所有内容都是正确的;

                    function isSSL() {
                    
                        $https = filter_input(INPUT_SERVER, 'HTTPS');
                        $port = filter_input(INPUT_SERVER, 'SERVER_PORT');
                        if ($https) {
                    
                            if ($https == 1) {
                                return true;
                            } elseif ($https == 'on') {
                                return true;
                            }
                        } elseif ($port == '443') {
                            return true;
                        }
                    
                        return false;
                    }
                    

                    【讨论】:

                      【解决方案22】:

                      通过阅读所有以前的帖子制作我自己的功能:

                      public static function isHttps()
                      {
                          if (array_key_exists("HTTPS", $_SERVER) && 'on' === $_SERVER["HTTPS"]) {
                              return true;
                          }
                          if (array_key_exists("SERVER_PORT", $_SERVER) && 443 === (int)$_SERVER["SERVER_PORT"]) {
                              return true;
                          }
                          if (array_key_exists("HTTP_X_FORWARDED_SSL", $_SERVER) && 'on' === $_SERVER["HTTP_X_FORWARDED_SSL"]) {
                              return true;
                          }
                          if (array_key_exists("HTTP_X_FORWARDED_PROTO", $_SERVER) && 'https' === $_SERVER["HTTP_X_FORWARDED_PROTO"]) {
                              return true;
                          }
                          return false;
                      }
                      

                      【讨论】:

                        【解决方案23】:

                        我有机会进一步确定我要连接的站点是否支持 SSL(一个项目要求用户提供他们的 URL,我们需要验证他们是否已在 http 或 https 站点上安装了我们的 API 包)。

                        这是我使用的函数 - 基本上,只需通过 cURL 调用 URL 即可查看 https 是否有效!

                        function hasSSL($url) 
                        {
                            // take the URL down to the domain name
                            $domain = parse_url($url, PHP_URL_HOST);
                            $ch = curl_init('https://' . $domain);
                            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD'); //its a  HEAD
                            curl_setopt($ch, CURLOPT_NOBODY, true);          // no body
                            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);  // in case of redirects
                            curl_setopt($ch, CURLOPT_VERBOSE, 0); //turn on if debugging
                            curl_setopt($ch, CURLOPT_HEADER, 1);     //head only wanted
                            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);    // we dont want to wait forever
                            curl_exec($ch);
                            $header = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                            if ($header === 200) {
                                return true;
                            }
                            return false;
                        }
                        

                        这是我发现的最可靠的方法,不仅可以确定您是否使用 https(如问题所要求的那样),而且可以(甚至应该)使用 https。

                        注意:有可能(虽然不太可能......)一个网站可能有不同的 http 和 https 页面(所以如果你被告知使用 http,也许你不需要更改......)大多数网站都是相同的,可能应该自己重新路由,但是这个额外的检查有它的用途(当然正如我所说,在用户输入他们的网站信息并且你想从服务器端确保的项目中)

                        【讨论】:

                          【解决方案24】:

                          这就是我找到解决这个问题的方法

                          $https = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'on') === 0 ||
                                  !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
                                      strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0;
                          
                          return ($https) ? 'https://' : 'http://';
                          

                          【讨论】:

                            【解决方案25】:

                            我在这里使用了主要建议,当未设置 HTTPS 时,我对日志中的“PHP 通知”感到恼火。你可以通过使用null-coalescing operator“??”来避免它:

                            if( ($_SERVER['HTTPS'] ?? 'off') == 'off' ) {
                                // redirect
                            }
                            

                            (注意:在 php v7 之前不可用)

                            【讨论】:

                              【解决方案26】:

                              如果您无法控制 Web 服务器并且不知道设置了哪些变量,请上传此 php 以查找:

                              <?php
                              echo "<br>1 ".$_SERVER["HTTPS"];
                              echo "<br>2 ".$_SERVER["SERVER_PORT"];
                              echo "<br>3 ".$_SERVER["HTTP_X_FORWARDED_PROTO"];
                              echo "<br>4 ".$_SERVER["HTTP_X_FORWARDED_SSL"];
                              echo "<br>5 ".$_SERVER["HTTP_HTTPS"];
                              echo "<br>6 ".$_SERVER["REQUEST_SCHEME"];
                              ?>
                              
                              <html>
                              <body>
                              <br>
                              Just cruising
                              </body>
                              </html>
                              

                              【讨论】:

                                【解决方案27】:

                                我知道这个答案已经晚了,但我结合了一堆答案并制作了一个适用于所有用例的简单函数。

                                试试这个:

                                function is_ssl(){
                                if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO']=="https"){ return true; }
                                elseif(isset($_SERVER['HTTPS'])){ return true; }
                                elseif($_SERVER['SERVER_PORT'] == 443){ return true; }
                                else{ return false; }
                                }
                                

                                那么就用if,例如:

                                if(is_ssl()){
                                // WHAT TO DO IF IT IS SSL / HTTPS
                                }else{
                                // WHAT TO DO IF IT IS NOT SSL / HTTPS
                                }
                                

                                此代码适用于 Cloudflare、共享托管服务提供商等。

                                享受吧。

                                【讨论】:

                                  【解决方案28】:

                                  我将 cloudflare 用于我的系统。我必须访问 $_SERVER['HTTP_CF_VISITOR'] 值。

                                  $isSsl = false; 
                                      if (isset($_SERVER['HTTP_CF_VISITOR'])) {
                                          $cfDecode = json_decode($_SERVER['HTTP_CF_VISITOR']);
                                          if (!empty($cfDecode) && !empty($cfDecode->scheme) && $cfDecode->scheme == 'https') {
                                              $isSsl = true;
                                          }
                                      }
                                  var_dump($isSsl);
                                  

                                  【讨论】:

                                    猜你喜欢
                                    • 1970-01-01
                                    • 2016-09-18
                                    • 1970-01-01
                                    • 2014-06-12
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    相关资源
                                    最近更新 更多