Como descobrir se você está usando HTTPS sem $ _SERVER ['HTTPS']


190

Eu já vi muitos tutoriais online que dizem que você precisa verificar $_SERVER['HTTPS']se o servidor está conectado à conexão com HTTPS. Meu problema é que, em alguns servidores que uso, $_SERVER['HTTPS']é uma variável indefinida que resulta em um erro. Existe outra variável que posso verificar que sempre deve ser definida?

Só para esclarecer, atualmente estou usando este código para resolver se é uma conexão HTTPS:

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

Por acaso, os servidores em que $ _SERVER ['HTTPS'] é indefinido estão sendo executados em HTTPS?
Freddy

Na verdade, um deles é o meu servidor WAMP em casa. E não acredito que esteja sendo executado em HTTPS.
Tyler Carter

@TylerCarter, um método alternativo é usar Securecookies. Tenha cuidado com as dicas embora.
Pacerier 6/03/15

Respostas:


280

Isso sempre deve funcionar mesmo quando $_SERVER['HTTPS']está indefinido:

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

O código é compatível com o IIS.

Na documentação do PHP.net e nos comentários dos usuários :

1) Defina como um valor não vazio se o script foi consultado através do protocolo HTTPS.

2) Observe que ao usar o ISAPI com o IIS, o valor será "desativado" se a solicitação não tiver sido feita através do protocolo HTTPS. (O mesmo comportamento foi relatado para o IIS7 executando o PHP como um aplicativo Fast-CGI).

Além disso, os servidores Apache 1.x (e instalações quebradas) podem não ter sido $_SERVER['HTTPS']definidos, mesmo que você se conecte com segurança. Apesar de não ser garantido, conexões na porta 443 são, por convenção , provavelmente usando Secure Sockets , portanto, a verificação de porta adicional.

Nota adicional: se houver um balanceador de carga entre o cliente e o servidor, esse código não testará a conexão entre o cliente e o balanceador de carga, mas a conexão entre o balanceador de carga e o servidor. Para testar a conexão anterior, você precisaria testar usando o HTTP_X_FORWARDED_PROTOcabeçalho, mas é muito mais complexo; veja os comentários mais recentes abaixo desta resposta.


50
Nb: porta 443 não conexão garantia é criptografado
ErichBSchulz

2
@DavidRodrigues Isso não é verdade. Você pode usar HTTP / HTTPS sobre qualquer porta que desejar. getservbyname()é apenas uma referência, não a realidade, e não garante, de forma alguma, que o HTTPS esteja sendo executado na porta 443.
Brad

1
Eu tive um pequeno problema com $_SERVER['SERVER_PORT'] !== 443tive que converter $_SERVER['SERVER_PORT]para um número inteiro como este:intval($_SERVER['SERVER_PORT]) !== 443
meconroy

1
1) A verificação da porta do servidor é um extra para servidores em folha, melhor removê-la se não for necessária. 2) Observe que é uma comparação solta na minha resposta;)
Gras Duplo

1
Mais um pequeno problema que encontrei hoje. O servidor estava retornando 'OFF' não 'off' - strtolower($_SERVER['HTTPS']) !== 'off'fez o truque.
Jhummel # 14/16

116

Minha solução (porque as condições padrão [$ _SERVER ['HTTPS'] == 'on'] não funcionam em servidores atrás de um balanceador de carga) é:

$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: um padrão de fato para identificar o protocolo de origem de uma solicitação HTTP, já que um proxy reverso (balanceador de carga) pode se comunicar com um servidor da Web usando HTTP, mesmo que a solicitação para o proxy reverso seja HTTPS http: //en.wikipedia. org / wiki / List_of_HTTP_header_fields # Common_non-standard_request_headers


4
Esta é a solução se você usar o proxy reverso do verniz.
Reto Zahner

1
Meu problema foi resolvido com esta solução. (PHP - AWS Elastic beanstalk)) #
4848347

Esta é a solução se você usar balanceadores de carga.
Abhishek Saini

Em que tipo de arquivo você está inserindo isso? Estou assumindo que isso não está no arquivo .htaccess?
Jordânia

5
Isso também funciona para o HTTPS gratuito fornecido pelo CloudFlare.
AnthonyVO 28/01

82

Chacha, de acordo com a documentação do PHP: "Defina como um valor não vazio se o script foi consultado através do protocolo HTTPS". Portanto, sua declaração if retornará false em muitos casos em que o HTTPS está realmente ativado. Você deseja verificar se $_SERVER['HTTPS']existe e não está vazio. Nos casos em que o HTTPS não está definido corretamente para um determinado servidor, você pode tentar verificar se $_SERVER['SERVER_PORT'] == 443.

Mas observe que alguns servidores também definirão $_SERVER['HTTPS']um valor não vazio, portanto, verifique também essa variável.

Referência: documentação $_SERVERe $HTTP_SERVER_VARS[obsoleta]


12
use $ _SERVER ['SERVER_PORT'] pode ser complicado ... por exemplo, o ispconfig usa a porta 81 como porta segura, então digamos que 443 é a porta "padrão" para o SSL.
Gabriel Sosa

@ Gabriel Sosa - É verdade, mas as advertências podem ser tratadas caso a caso. A resposta do @ hobodave funcionará para a maioria.
Tim Post

Observe que isso não funcionará atrás de um proxy reverso. Pode-se considerar verificar HTTP_X_FORWARDED_PROTOou HTTP_X_FORWARDED_SSLtambém.
paolo 14/12

1
Concordo que esse último recurso deve ser o número da porta, então aqui está o meu cheque: (((isset($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) == 'on')) || ((isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'))) que não inclui o cheque da porta. Sinta-se livre para adicionar. :-)
Roland

O @paolo atrás de um proxy reverso SetEnvIf X-Forwarded-SSL on HTTPS=onfará o truque. Mas isso não vai funcionar com REQUEST_SCHEMEcomo resultado em php parece melhor usar$_SERVER['HTTPS']
Antony Gibbs

14

Isso também funciona quando $_SERVER['HTTPS']está indefinido

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

2
São alguns servidores em que $_SERVER['HTTPS']ainda não está definido o https está ativado. Que tal isso?
John Max

1
@JohnMax SERVER_PORTé definido sempre que resolve o problema indefinido deHTTPS
Thamaraiselvam

11

Acabei de ter um problema em que eu estava executando o servidor usando o Apache mod_ssl, mas um phpinfo () e um var_dump ($ _SERVER) mostraram que o PHP ainda acha que estou na porta 80.

Aqui está minha solução alternativa para qualquer pessoa com o mesmo problema ....

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

A linha que vale a pena notar é a linha SetEnv. Com isso em vigor e após uma reinicialização, você deve ter a variável de ambiente HTTPS com a qual sempre sonhou


5
É melhor garantir que o HTTPS esteja realmente funcionando; isso fará o servidor mentir para você, se não for.
precisa saber é

Você também precisa do módulo SetEnv para que isso funcione. Está ativado por padrão, mas você nunca sabe o que um administrador de servidor pode desativar.
toon81

Muito útil no caso de você estar na janela de encaixe através do proxy reverso. Obrigado!
Dikirill 03/03/19

8

Fazendo minha própria função lendo todas as postagens anteriores:

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

Se você estiver usando o Apache, poderá sempre contar com

$_SERVER["REQUEST_SCHEME"]

para verificar o esquema do URL solicitado. Mas, como mencionado em outras respostas, é prudente verificar outros parâmetros antes de assumir que o SSL está realmente sendo usado.


funciona no XAMPP, mas não no centos / apache2 + PHP ... portanto, não é confiável.
Firas Abd Alrahman 06/04

5

A resposta REAL: pronta para copiar e colar em um script [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_URIprotocolagora está correto e pronto para ser usado; exemplo $site=$pv_URIprotocol.$_SERVER["SERVER_NAME"]. Naturalmente, a string também pode ser substituída por TRUE e FALSE. PV significa PortalPress Variable, pois é uma cópia e colagem direta que sempre funcionará. Esta peça pode ser usada em um script de produção.


3

Não acho que adicionar uma porta seja uma boa ideia - especialmente quando você tem muitos servidores com diferentes compilações. isso apenas acrescenta mais uma coisa para lembrar de mudar. olhando os documentos, acho que a última linha de kaisers é muito boa, de modo que:

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

parece perfeitamente suficiente.


3

O único método confiável é o descrito por 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://");

Considere o seguinte: Você está usando o nginx com fastcgi, por padrão (debian, ubuntu) fastgi_params contém a diretiva:

fastcgi_param HTTPS $ https;

se você NÃO estiver usando SSL, ele será traduzido como valor vazio, não 'off', não 0 e você estará condenado.

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


3

Acho esses parâmetros aceitáveis ​​também e, mais do que provavelmente, não têm falsos positivos ao trocar de servidor da 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*/}

Isso não diz nada sobre o uso de HTTPS com um balanceador de carga / proxy.
Brad

3

Maneira mais curta que estou usando:

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

Se https for usado, $ secure_connection é true.


echo (!empty($_SERVER['HTTPS'])?'https':'http');dá-lhe httpouhttps
Xavi Esteve

, torná-lo(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie

2

Você pode verificar $_SERVER['SERVER_PORT']como o SSL normalmente é executado na porta 443, mas isso não é infalível.


$ _SERVER ['SERVER_PORT'], no entanto.
Tyler Carter

2

O que você acha disso?

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

Sim existe. Se você confiar apenas em empty (), o PHP sairá com erro se não houver um índice 'HTTPS'.
RMC toni

3
"empty () é essencialmente o equivalente concisa para isset ($ var) || $ var == false!" - php.net/manual/en/function.empty.php
John Magnolia

2
Você está certo. Engraçado, eu perdi essa. Eu sempre pensei que vazio () falhará se a variável não existir.
RMC toni

2

No meu servidor (Ubuntu 14.10, Apache 2.4, php 5.5) a variável $_SERVER['HTTPS']não é definida quando o script php é carregado via https. Eu não sei o que está errado. Mas as seguintes linhas no .htaccessarquivo corrigem esse problema:

RewriteEngine on

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

1

Aqui está uma função reutilizável que venho usando há algum tempo. HTH.

Nota: O valor de HTTPS_PORT (que é uma constante personalizada no meu código) pode variar em seu ambiente, por exemplo, pode ser 443 ou 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

só por interesse, o chrome canary no momento envia

HTTPS : 1

para o servidor, e dependendo de como o servidor estiver configurado pode significar que você retornará as seguintes

HTTPS : 1, on

Isso interrompeu nosso aplicativo porque estávamos testando se estava ativado, o que obviamente não está. No momento, apenas o cromo canário parece fazer isso, mas vale a pena notar que as coisas do canário geralmente caem no cromo "normal" pouco tempo depois.


1

Se você usar o nginx como sistema de balanceamento de carga, verifique $ _SERVER ['HTTP_HTTPS'] == 1 outras verificações falharão no 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;

O código está verificando tudo o que é possível e funciona também no servidor web IIS. O Chrome desde a v44 não define o cabeçalho HTTP: 1, portanto, verificar HTTP_HTTPS está OK. Se esse código não corresponder a https, significa que o servidor da web ou o servidor proxy está mal configurado. O próprio Apache define o sinalizador HTTPS corretamente, mas pode haver um problema ao usar o proxy (por exemplo, nginx). Você deve definir algum cabeçalho no host virtual nginx https

proxy_set_header   X-HTTPS 1;

e use algum módulo Apache para definir o sinalizador HTTPS corretamente, procurando por X-HTTPS no proxy. Pesquise mod_fakessl, mod_rpaf etc.


0

Se você estiver usando o balanceador de carga do Incapsula, precisará usar um IRule para gerar um cabeçalho personalizado para o seu servidor. Criei um cabeçalho HTTP_X_FORWARDED_PROTO que é igual a "http" se a porta estiver definida como 80 e "https" se for igual a 443.


0

Eu adicionaria um filtro global para garantir que tudo o que estou verificando esteja correto;

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

Tenho a oportunidade de dar um passo adiante e determinar se o site ao qual estou me conectando é compatível com SSL (um projeto solicita ao URL o usuário e precisamos verificar se ele instalou nosso pacote de API em um site http ou https).

Aqui está a função que eu uso - basicamente, basta ligar para o URL via cURL para ver se o https funciona!

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

Esta é a maneira mais confiável que eu encontrei, não apenas para descobrir se você está usando https (como a pergunta pergunta), mas se você PODERIA (ou DEVE) usar https.

NOTA: é possível (embora não seja muito provável ...) que um site possa ter páginas http e https diferentes (por isso, se lhe for pedido para usar http, talvez você não precise alterar ..) A grande maioria dos sites são os mesmos e provavelmente devem redirecionar a si mesmos, mas essa verificação adicional tem seu uso (certamente como eu disse, no projeto em que o usuário insere as informações do site e você quer ter certeza do lado do servidor)


0

É assim que eu acho resolver isso

$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

Usei a sugestão principal aqui e fiquei irritado com o "Aviso sobre PHP" nos logs quando o HTTPS não estava definido. Você pode evitá-lo usando o operador de coalescência nula "??":

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

(Nota: não disponível antes do php v7)


-7

Conforme a postagem do hobodave: "Defina como um valor não vazio se o script foi consultado através do protocolo HTTPS".

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

Faça isso(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.