如何在不使用$ _SERVER ['HTTPS']的情况下查看是否使用HTTPS

时间:2009-07-23 23:42:49

标签: php https

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

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

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

27 个答案:

答案 0 :(得分:261)

即使$_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)请注意,在IIS中使用ISAPI时,如果请求不是通过HTTPS协议进行的,则值将为“off”。 (对于运行PHP作为Fast-CGI应用程序的IIS7,已经报告了相同的行为。)

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

答案 1 :(得分:107)

我的解决方案(因为标准条件[$ _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

答案 2 :(得分:81)

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

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

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

答案 3 :(得分:13)

$_SERVER['HTTPS']未定义

时,这也有效
if( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443 ){
    //enable secure connection
}

答案 4 :(得分:9)

我刚刚遇到一个问题,我使用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环境变量

答案 5 :(得分:6)

通过阅读以前的所有帖子来创建自己的功能:

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;
}

答案 6 :(得分: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 Variable,因为它是一个永远有效的直接复制粘贴。这件作品可用于制作剧本。

答案 7 :(得分:3)

我不认为添加端口是个好主意 - 特别是当您有许多具有不同构建的服务器时。这只是增加了一件事要记住改变。看看doc我认为最后一行的kaisers非常好,所以:

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

看起来非常完美。

答案 8 :(得分:3)

唯一可靠的方法是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 :(得分:3)

我发现这些参数也可以接受,并且在切换网络服务器时可能没有误报。

  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*/}
    

答案 10 :(得分:2)

在我的服务器(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]

答案 11 :(得分:2)

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

尝试一下:

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(is_ssl()){
// WHAT TO DO IF IT IS SSL / HTTPS
}else{
// WHAT TO DO IF IT IS NOT SSL / HTTPS
}

此代码可与Cloudflare,共享托管服务提供商等配合使用。

享受。

答案 12 :(得分:2)

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

答案 13 :(得分:2)

你怎么看待这个?

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

答案 14 :(得分:2)

我使用的最短路:

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

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

答案 15 :(得分:1)

只是为了兴趣,铬金丝雀此刻发送

HTTPS : 1

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

HTTPS : 1, on

这打破了我们的申请,因为我们正在测试是否开启,这显然不是。 目前,只有铬金丝雀似乎这样做,但值得注意的是金丝雀的东西通常落在&#34;正常&#34;不久之后铬。

答案 16 :(得分:1)

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

答案 17 :(得分:1)

这是我已经使用了一段时间的可重复使用的功能。 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;
}

答案 18 :(得分:1)

$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不匹配,则表示您的Web服务器或代理服务器配置不当。 Apache本身正确设置HTTPS标志,但是当您使用代理(例如nginx)时可能会出现问题。您必须在nginx https虚拟主机中设置一些标头

proxy_set_header   X-HTTPS 1;

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

答案 19 :(得分:0)

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

答案 20 :(得分:0)

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

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;
}

答案 21 :(得分:0)

我有机会更进一步确定我连接的网站是否支持SSL(一个项目要求用户提供他们的网址,我们需要验证他们是否已在http或https网站)。

这是我使用的功能 - 基本上,只需通过cURL调用URL,看看h​​ttps是否有效!

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,也许你不需要改变..)绝大多数网站是相同的,可能应该自己重新路由,但这个额外的检查有其用途(当然,正如我所说,在用户输入其网站信息的项目中,你想从服务器端确保)

答案 22 :(得分:0)

这就是我找到解决方法的方法

$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://';

答案 23 :(得分:0)

我在这里使用了主要建议,并且在未设置HTTPS的情况下,日志中的“ PHP通知”使我很恼火。您可以使用null-coalescing operator“ ??”:

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

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

答案 24 :(得分:0)

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

q1.axis

答案 25 :(得分:0)

我的系统使用 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);

答案 26 :(得分:-7)

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

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