Cómo saber si estás usando HTTPS sin $ _SERVER ['HTTPS']

190

He visto muchos tutoriales en línea que dicen que debe verificar $_SERVER['HTTPS']si el servidor está conectado con HTTPS. Mi problema es que en algunos de los servidores que uso, $_SERVER['HTTPS']es una variable indefinida que produce un error. ¿Hay otra variable que pueda verificar que siempre debe definirse?

Para ser claros, actualmente estoy usando este código para resolver si es una conexión HTTPS:

if(isset($_SERVER['HTTPS'])) {
    if ($_SERVER['HTTPS'] == "on") {
        $secure_connection = true;
    }
}
Tyler Carter
fuente
¿Por casualidad, los servidores donde $ _SERVER ['HTTPS'] no está definido se ejecutan en HTTPS?
Freddy
En realidad, uno de ellos es el servidor WAMP de mi casa. Y no creo que se esté ejecutando en HTTPS.
Tyler Carter el
@TylerCarter, un método alternativo es usar Securecookies. Pero ten cuidado con las trampas.
Pacerier

Respuestas:

280

Esto siempre debería funcionar incluso cuando $_SERVER['HTTPS']no está definido:

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

El código es compatible con IIS.

De la documentación de PHP.net y los comentarios de los usuarios :

1) Establezca un valor no vacío si el script fue consultado a través del protocolo HTTPS.

2) Tenga en cuenta que cuando se usa ISAPI con IIS, el valor estará "apagado" si la solicitud no se realizó a través del protocolo HTTPS. (Se ha informado el mismo comportamiento para IIS7 que ejecuta PHP como una aplicación Fast-CGI).

Además, los servidores Apache 1.x (e instalaciones rotas) pueden no haberse $_SERVER['HTTPS']definido incluso si se conectan de forma segura. Aunque no está garantizado, conexiones en el puerto 443 son, por convención , probablemente utilizando conexiones seguras , por lo tanto, la comprobación de puerto adicional.

Nota adicional: si hay un equilibrador de carga entre el cliente y su servidor, este código no prueba la conexión entre el cliente y el equilibrador de carga, sino la conexión entre el equilibrador de carga y su servidor. Para probar la conexión anterior, tendría que probar usando el HTTP_X_FORWARDED_PROTOencabezado, pero es mucho más complejo de hacer; ver los últimos comentarios debajo de esta respuesta.

Gras Double
fuente
50
Nota: el puerto 443 no garantiza que la conexión esté encriptada
ErichBSchulz
2
@DavidRodrigues Eso no es cierto. Puede usar HTTP / HTTPS en cualquier puerto que desee. getservbyname()es solo una referencia, no una realidad, y de ninguna manera garantiza que HTTPS se ejecute en el puerto 443.
Brad
1
Tuve un pequeño problema con el $_SERVER['SERVER_PORT'] !== 443que tuve que convertir $_SERVER['SERVER_PORT]a un número entero así:intval($_SERVER['SERVER_PORT]) !== 443
meconroy
1
1) La verificación del puerto del servidor es un extra para los servidores con hojas, lo mejor es eliminarlo si no es necesario. 2) Note que es una comparación floja en mi respuesta;)
Gras Double
1
Un pequeño problema más que encontré hoy. El servidor estaba regresando 'APAGADO' no 'apagado': strtolower($_SERVER['HTTPS']) !== 'off'funcionó.
jhummel
116

Mi solución (porque las condiciones estándar [$ _SERVER ['HTTPS'] == 'on'] no funcionan en servidores detrás de un equilibrador de carga) es:

$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: un estándar de facto para identificar el protocolo de origen de una solicitud HTTP, ya que un proxy inverso (equilibrador de carga) puede comunicarse con un servidor web utilizando HTTP incluso si la solicitud al proxy inverso es HTTPS http: //en.wikipedia. org / wiki / List_of_HTTP_header_fields # Common_non-standard_request_headers

temuraru
fuente
44
Esta es la solución si utiliza el proxy inverso de barniz.
Reto Zahner
1
Mi problema se resolvió con esta solución. (PHP - AWS Elastic beanstalk)
usuario4826347
Esta es la solución si usa equilibradores de carga.
Abhishek Saini
¿En qué tipo de archivo estás metiendo esto? ¿Asumo que esto no está en el archivo .htaccess?
Jordania
55
Esto también funciona para el HTTPS gratuito proporcionado por CloudFlare.
AnthonyVO
82

Chacha, según la documentación de PHP: "Establezca un valor no vacío si el script fue consultado a través del protocolo HTTPS". Por lo tanto, su declaración if allí devolverá false en muchos casos en los que HTTPS está activado. Deberá verificar que $_SERVER['HTTPS']existe y que no está vacío. En los casos en que HTTPS no esté configurado correctamente para un servidor determinado, puede intentar verificar si $_SERVER['SERVER_PORT'] == 443.

Pero tenga en cuenta que algunos servidores también se establecerán $_SERVER['HTTPS']en un valor no vacío, así que asegúrese de verificar también esta variable.

Referencia: documentación $_SERVERy $HTTP_SERVER_VARS[en desuso]

hobodave
fuente
12
usar $ _SERVER ['SERVER_PORT'] puede ser complicado ... por ejemplo, ispconfig usa el puerto 81 como puerto seguro, así que digamos que 443 es el puerto "predeterminado" para ssl.
Gabriel Sosa el
@ Gabriel Sosa: es cierto, pero las advertencias se pueden abordar caso por caso La respuesta de @ hobodave funcionará para la mayoría.
Tim Post
Tenga en cuenta que esto no funcionará detrás de un proxy inverso. Uno podría considerar verificar HTTP_X_FORWARDED_PROTOo HTTP_X_FORWARDED_SSLtambién.
paolo
1
Estoy de acuerdo con que el último recurso debería ser el número de puerto, así que aquí está mi cheque: (((isset($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) == 'on')) || ((isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'))) que no incluye el chequeo de puerto. Siéntase libre de agregar. :-)
Roland
@paolo detrás de un proxy inverso SetEnvIf X-Forwarded-SSL on HTTPS=onhará el truco. Pero esto no funcionará REQUEST_SCHEMEcomo resultado en php parece mejor usar$_SERVER['HTTPS']
Antony Gibbs
14

Esto también funciona cuando $_SERVER['HTTPS']no está definido

if( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443 ){
    //enable secure connection
}
Thamaraiselvam
fuente
2
Son algunos servidores donde $_SERVER['HTTPS']aún no está definido https está habilitado. Qué hay sobre eso ?
John Max
1
@JohnMax SERVER_PORTsiempre se define que resuelve el problema indefinido deHTTPS
Thamaraiselvam
11

Acabo de tener un problema en el que estaba ejecutando el servidor usando Apache mod_ssl, pero un phpinfo () y un var_dump ($ _SERVER) mostraron que PHP todavía cree que estoy en el puerto 80.

Aquí está mi solución para cualquiera con el mismo problema ...

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

La línea que vale la pena señalar es la línea SetEnv. Con esto en su lugar y después de un reinicio, debe tener la variable de entorno HTTPS que siempre soñó.

Thomas-Peter
fuente
55
Mejor asegúrese de que HTTPS esté realmente funcionando; eso hará que el servidor te mienta si no es así.
Brad Koch
También necesita el módulo SetEnv para que esto funcione. Está habilitado de forma predeterminada, pero nunca se sabe lo que un administrador del servidor podría deshabilitar.
toon81
Muy útil en caso de que esté en la ventana acoplable a través del proxy inverso. ¡Gracias!
dikirill
8

Haciendo mi propia función de leer todas las publicaciones 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;
}
abadejo
fuente
7

Si está utilizando Apache, siempre puede contar con

$_SERVER["REQUEST_SCHEME"]

para verificar el esquema de la URL solicitada. Pero, como se mencionó en otras respuestas, es prudente verificar otros parámetros antes de asumir que realmente se está utilizando SSL.

Ed de Almeida
fuente
funciona en XAMPP pero no en centos / apache2 + PHP ... por lo que no es confiable.
Firas Abd Alrahman
5

La respuesta REAL: listo para copiar y pegar en un 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_URIprotocolahora es correcto y está listo para ser usado; ejemplo $site=$pv_URIprotocol.$_SERVER["SERVER_NAME"]. Naturalmente, la cadena podría reemplazarse con VERDADERO y FALSO también. PV significa PortalPress Variable, ya que es una copia y pega directa que siempre funcionará. Esta pieza se puede usar en un guión de producción.

Igor M. - PortalPress.org
fuente
3

No creo que agregar un puerto sea una buena idea, especialmente cuando tienes muchos servidores con diferentes compilaciones. eso solo agrega una cosa más para recordar cambiar. mirando los documentos, creo que la última línea de kaisers es bastante buena, por lo que:

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

Parece lo suficientemente perfecto.

sp3c1
fuente
3

El único método confiable es el 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 lo siguiente: está utilizando nginx con fastcgi, por defecto (debian, ubuntu) fastgi_params contiene la directiva:

fastcgi_param HTTPS $ https;

si NO está utilizando SSL, se traduce como un valor vacío, no 'apagado', no 0 y está condenado.

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

Galvani
fuente
3

Considero que estos parámetros también son aceptables y es muy probable que no tengan falsos positivos al cambiar de servidor 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*/}
Werezywolf
fuente
Esto no le dice nada sobre el uso de HTTPS con un equilibrador de carga / proxy.
Brad
3

El camino más corto que estoy usando:

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

Si se usa https, entonces $ secure_connection es verdadero.

Markus Zeller
fuente
echo (!empty($_SERVER['HTTPS'])?'https':'http');te da httpohttps
Xavi Esteve
, (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
hazlo
2

Puede verificar $_SERVER['SERVER_PORT']que SSL normalmente se ejecuta en el puerto 443, pero esto no es infalible.

pix0r
fuente
Sin embargo, $ _SERVER ['SERVER_PORT'] sí.
Tyler Carter
2

¿Qué piensas de esto?

if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
    $scheme = 'https';
else
    $scheme = 'http';
toni rmc
fuente
Sí hay. Si confía solo en empty () PHP saldrá con error si no hay un índice 'HTTPS'.
toni rmc
3
"empty () es esencialmente el conciso equivalente a! isset ($ var) || $ var == false" - php.net/manual/en/function.empty.php
John Magnolia
2
Tienes razón. Es curioso que me perdí esa. Siempre pensé que empty () fallará si la variable no existe.
toni rmc
2

En mi servidor (Ubuntu 14.10, Apache 2.4, php 5.5) la variable $_SERVER['HTTPS']no se establece cuando el script php se carga a través de https. No se que esta mal. Pero las siguientes líneas en el .htaccessarchivo solucionan este problema:

RewriteEngine on

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

Aquí hay una función reutilizable que he estado usando durante un tiempo. HTH

Nota: El valor de HTTPS_PORT (que es una constante personalizada en mi código) puede variar según su entorno, por ejemplo, puede ser 443 u 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;
}
crmpicco
fuente
1

solo por interés, canario de cromo en este momento envía

HTTPS : 1

al servidor, y dependiendo de cómo esté configurado el servidor puede significar que recupere lo siguiente

HTTPS : 1, on

Esto rompió nuestra aplicación porque estábamos probando si estaba activado, lo que obviamente no es así. Por el momento, solo el cromo canario parece hacer esto, pero vale la pena señalar que las cosas del canario generalmente aterrizan en el cromo "normal" un poco más tarde.

John Smith
fuente
1

Si utiliza nginx como sistema de equilibrio de carga, compruebe $ _SERVER ['HTTP_HTTPS'] == 1 otras comprobaciones fallarán para SSL.

base
fuente
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;

El código está comprobando todo lo posible y funciona también en el servidor web IIS. Chrome desde v44 no establece el encabezado HTTP: 1, por lo que verificar HTTP_HTTPS está bien. Si este código no coincide con https, significa que su servidor web o servidor proxy está mal configurado. Apache mismo establece el indicador HTTPS correctamente, pero puede haber problemas cuando usa proxy (por ejemplo, nginx). Debe establecer un encabezado en el host virtual https nginx

proxy_set_header   X-HTTPS 1;

y use algún módulo de Apache para configurar el indicador HTTPS correctamente buscando X-HTTPS desde el proxy. Busque mod_fakessl, mod_rpaf, etc.

mikep
fuente
0

Si está utilizando el equilibrador de carga de Incapsula, deberá usar una IRule para generar un encabezado personalizado para su servidor. Creé un encabezado HTTP_X_FORWARDED_PROTO que es igual a "http" si el puerto está configurado en 80 y "https" si es igual a 443.

Nadav
fuente
0

Agregaría un filtro global para asegurar que todo lo que estoy verificando sea correcto;

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;
}
Rodrigo Manara
fuente
0

Tengo la oportunidad de ir un paso más allá y determinar si el sitio al que me estoy conectando es compatible con SSL (un proyecto le pide al usuario su URL y debemos verificar que haya instalado nuestro paquete API en un sitio http o https).

Aquí está la función que uso: ¡básicamente, solo llame a la URL a través de cURL para ver si 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 es la forma más confiable que he encontrado para descubrir no solo SI está usando https (como lo indica la pregunta), sino si PUEDE (o incluso DEBERÍA) estar usando https.

NOTA: es posible (aunque no muy probable ...) que un sitio tenga páginas http y https diferentes (por lo tanto, si le dicen que use http, tal vez no necesite cambiar ...) La gran mayoría de los sitios son los mismos, y probablemente deberían redirigirlo usted mismo, pero esta verificación adicional tiene su uso (ciertamente, como dije, en el proyecto donde el usuario ingresa la información de su sitio y desea asegurarse desde el lado del servidor)

Soporte CFP
fuente
0

Así es como encuentro resolver esto

$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://';
Fhulufhelo Mokhomi
fuente
0

Utilicé la sugerencia principal aquí y me molestó el "Aviso PHP" en los registros cuando HTTPS no estaba configurado. Puede evitarlo utilizando el operador de fusión nula "??":

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

(Nota: no disponible antes de php v7)

garafajon
fuente
-7

Según la publicación de hobodave: "Establecer en un valor no vacío si el script fue consultado a través del protocolo HTTPS".

if (!empty($_SERVER['HTTPS']))
{
    $secure_connection = true;
}
usuario128026
fuente
hacerlo(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie