如何确定您是否使用不带$ _SERVER ['HTTPS']的HTTPS


190

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

为了清楚起见,我目前正在使用以下代码来确定它是否是HTTPS连接:

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

碰巧,那些未定义$ _SERVER ['HTTPS']的服务器是否正在HTTPS上运行?
弗雷迪

实际上,其中之一是我的家用WAMP服务器。而且我不认为它在HTTPS上运行。
泰勒·卡特

@TylerCarter,另一种方法是使用Securecookie。但是要小心陷阱。
Pacerier'3

Answers:


280

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

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

该代码与IIS兼容。

PHP.net文档和用户评论

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

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

另外,$_SERVER['HTTPS']即使安全连接,Apache 1.x服务器(以及损坏的安装)也可能尚未定义。尽管不能保证,但按照惯例,端口443上的连接可能使用安全套接字,因此需要进行额外的端口检查。

附加说明:如果客户端和服务器之间存在负载平衡器,则此代码不会测试客户端和负载平衡器之间的连接,而是测试负载平衡器和服务器之间的连接。要测试以前的连接,您必须使用HTTP_X_FORWARDED_PROTO标头进行测试,但这要复杂得多。查看此答案下方的最新评论


50
Nb:端口443不保证连接已加密
ErichBSchulz 2014年

2
@DavidRodrigues事实并非如此。您可以在所需的任何端口上使用HTTP / HTTPS。 getservbyname()只是参考,不是现实,并且不以任何方式保证HTTPS在端口443上运行。–
Brad

1
我有一个小问题,$_SERVER['SERVER_PORT'] !== 443我必须将$_SERVER['SERVER_PORT]intval($_SERVER['SERVER_PORT]) !== 443
强制转换为

1
1)服务器端口检查对于小型服务器来说是额外的,如果不需要,最好将其删除。2)请注意,这是我的回答中的一个松散比较;)
Gras Double

1
我今天遇到了另一个小问题。服务器返回“关闭”而不是“关闭”- strtolower($_SERVER['HTTPS']) !== 'off'做到了。
jhummel

116

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


4
如果您使用清漆反向代理,这就是解决方案。
Reto Zahner 2013年

1
此解决方案解决了我的问题。(PHP-AWS Elastic beanstalk)
2016年

如果使用负载平衡器,这就是解决方案。
Abhishek Saini

您要粘贴哪种类型的文件?我假设这不在.htaccess文件中?
约旦

5
这也适用于CloudFlare提供的免费HTTPS。
AnthonyVO

82

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

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

参考:的文档$_SERVER$HTTP_SERVER_VARS[不推荐使用]


12
使用$ _SERVER ['SERVER_PORT']可能很棘手...例如ispconfig使用端口81作为安全端口,因此可以说443是ssl的“默认”端口。
加布里埃尔·索萨

@Gabriel Sosa-是的,但是警告可以根据具体情况解决。@hobodave的答案适用于大多数情况。
蒂姆·波斯特

请注意,这在反向代理之后是行不通的。一个人可能会考虑检查HTTP_X_FORWARDED_PROTO还是HTTP_X_FORWARDED_SSL一样。
paolo

1
我同意最后的手段应该是端口号,所以这是我的检查:(((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']
Antony Gibbs

14

$_SERVER['HTTPS']未定义时也可以使用

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

2
它们是一些$_SERVER['HTTPS']尚未定义https的服务器。那个怎么样 ?
约翰·马克斯

1
@JohnMax SERVER_PORT始终被定义来解决未定义的问题HTTPS
Thamaraiselvam

11

我刚遇到一个问题,我使用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行。将其安装到位并重新启动后,您应该拥有您梦always以求的HTTPS环境变量


5
最好确保HTTPS确实有效;如果不是,那将使服务器对您说谎。
布拉德·科赫

此外,您还需要SetEnv模块才能使其正常工作。默认情况下已启用它,但您永远不知道服务器管理员可能禁用了什么。
toon81 2015年

如果您使用反向代理在docker上非常有用。谢谢!
dikirill

8

通过阅读之前的所有文章来发挥自己的作用:

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

7

如果您使用的是Apache,则可以始终依靠

$_SERVER["REQUEST_SCHEME"]

验证请求的URL的方案。但是,正如其他答案中所提到的那样,在假设实际使用SSL之前,请先验证其他参数,这是谨慎的做法。


它可以在XAMPP上运行,但不能在centos / apache2 + PHP上运行,因此不受信任。
Firas Abd Alrahman'4

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,因为它是直接复制粘贴,将始终有效。该作品可以在生产脚本中使用。


3

我认为添加端口不是一个好主意-特别是当您拥有许多具有不同内部版本的服务器时。这只是增加了一件要记住要改变的事情。查看文档,我认为改进程序的最后一行非常好,因此:

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

看起来足够完美。


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://");

请考虑以下事项:您正在将nginx与fastcgi一起使用,默认情况下(debian,ubuntu)fastgi_params包含指令:

fastcgi_param HTTPS $ https;

如果您不使用SSL,它将被转换为空值,而不是'off',而不是0,这注定了您的失败。

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


3

我发现这些参数也可以接受,并且更可能在切换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的任何信息。
布拉德2014年


2

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


但是$ _SERVER ['SERVER_PORT']可以。
泰勒·卡特

2

你觉得这怎么样?

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

就在这里。如果仅依靠empty(),则如果没有“ HTTPS”索引,PHP将会错误退出。
toni rmc

3
“ empty()本质上是等同于!isset($ var)|| $ var == false的简洁形式” -php.net/manual/en/function.empty.php
约翰·木兰

2
你是对的。有趣的是我错过了那个。我一直认为,如果变量不存在,empty()将失败。
toni rmc

2

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

RewriteEngine on

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

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

1

仅出于兴趣,此刻铬金丝雀发送

HTTPS : 1

到服务器,并且取决于服务器的配置方式,这意味着您会获得以下信息

HTTPS : 1, on

这破坏了我们的应用程序,因为我们正在测试是否启用(显然不是)。目前,只有铬金丝雀似乎可以做到这一点,但值得注意的是,金丝雀中的东西一般会在不久后降落到“普通”铬中。


1

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


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

proxy_set_header   X-HTTPS 1;

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


0

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


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

0

我有机会更进一步,确定我连接的站点是否支持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,则可能不需要更改。)绝大多数站点是相同的,可能应该自己重新路由,但是这种额外的检查有其用处(肯定是我说过的,在用户输入其站点信息并且您要确保从服务器端进行确认的项目中)


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

0

我在这里使用了主要建议,并且在未设置HTTPS的情况下对日志中的“ PHP通知”感到恼火。您可以使用null运算符 “ ??” 来避免这种情况:

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

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


-7

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

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

做到(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.