Как узнать, используете ли вы HTTPS без $ _SERVER ['HTTPS']

191

Я видел много онлайн-уроков, в которых говорится, что вам нужно проверить, защищено $_SERVER['HTTPS']ли соединение с сервером по протоколу HTTPS. Моя проблема в том, что на некоторых серверах, которые я использую, $_SERVER['HTTPS']это неопределенная переменная, которая приводит к ошибке. Могу ли я проверить еще одну переменную, которая всегда должна быть определена?

Просто чтобы быть ясно, я в настоящее время использую этот код, чтобы решить, если это соединение HTTPS:

if(isset($_SERVER['HTTPS'])) {
    if ($_SERVER['HTTPS'] == "on") {
        $secure_connection = true;
    }
}
Тайлер Картер
источник
Есть ли вероятность, что те серверы, на которых $ _SERVER ['HTTPS'] не определен, работают на HTTPS?
Фредди
Фактически, один из них - мой домашний сервер WAMP. И я не верю, что он работает на HTTPS.
Тайлер Картер
@TylerCarter, альтернативный метод - использовать Secureкуки. Будьте осторожны с ошибками, хотя.
Pacerier

Ответы:

281

Это всегда должно работать, даже когда $_SERVER['HTTPS']не определено:

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

Код совместим с IIS.

Из документации по PHP.net и комментариев пользователей :

1) Установите непустое значение, если скрипт запрашивался по протоколу HTTPS.

2) Обратите внимание, что при использовании ISAPI с IIS значение будет «выключено», если запрос не был сделан по протоколу HTTPS. (О том же поведении сообщалось для IIS7, выполняющего PHP как приложение Fast-CGI).

Кроме того, серверы Apache 1.x (и неработающие установки) могут не $_SERVER['HTTPS']определиться даже при безопасном подключении. Хотя это и не гарантировано, соединения через порт 443, по соглашению , скорее всего, используют безопасные сокеты , следовательно, дополнительная проверка порта.

Дополнительное примечание: если есть балансировщик нагрузки между клиентом и вашим сервером, этот код проверяет не соединение между клиентом и балансировщиком нагрузки, а соединение между балансировщиком нагрузки и вашим сервером. Чтобы протестировать предыдущее соединение, вы должны будете проверить, используя HTTP_X_FORWARDED_PROTOзаголовок, но сделать это гораздо сложнее; см последние комментарии ниже этого ответа.

Гра двухместный
источник
50
Примечание: порт 443 не гарантирует шифрование соединения
ErichBSchulz
2
@DavidRodrigues Это не правда. Вы можете использовать HTTP / HTTPS через любой порт, который вы хотите. getservbyname()это только ссылка, а не реальность, и никоим образом не гарантирует, что HTTPS работает через порт 443.
Брэд,
1
У меня была небольшая проблема, $_SERVER['SERVER_PORT'] !== 443я должен был привести $_SERVER['SERVER_PORT]к целому числу вот так:intval($_SERVER['SERVER_PORT]) !== 443
meconroy
1
1) Проверка порта сервера является дополнительной для листовых серверов, лучше удалить ее, если она не нужна. 2) Заметьте, что в моем ответе нет свободного сравнения;)
Gras Double
1
Еще одна маленькая проблема, с которой я столкнулся сегодня. Сервер возвращал «OFF», а не «off» - strtolower($_SERVER['HTTPS']) !== 'off'сделал свое дело.
jhummel
117

Мое решение (потому что стандартные условия [$ _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, даже если запрос обратного прокси-сервера - HTTPS http: //en.wikipedia. орг / вики / List_of_HTTP_header_fields # Common_non-standard_request_headers

temuraru
источник
4
Это решение, если вы используете лак обратного прокси.
Рето Захнер
1
Моя проблема была решена с этим решением. (PHP - AWS Elastic
beanstalk
Это решение, если вы используете балансировщики нагрузки.
Абхишек Сайни
К какому типу файла вы прикрепляете это? Я предполагаю, что это не в файле .htaccess?
Джордан
5
Это также работает для бесплатного HTTPS, предоставляемого CloudFlare.
AnthonyVO
82

Чача, согласно документации PHP: «Установите непустое значение, если скрипт запрашивался по протоколу HTTPS». Так что ваш оператор if во многих случаях вернет false, если HTTPS действительно включен. Вы хотите проверить, что $_SERVER['HTTPS']существует и не является пустым. В случаях, когда HTTPS не установлен правильно для данного сервера, вы можете попробовать проверить, если $_SERVER['SERVER_PORT'] == 443.

Но обратите внимание, что на некоторых серверах также устанавливается $_SERVER['HTTPS']непустое значение, поэтому обязательно проверьте и эту переменную.

Ссылка: Документация $_SERVERи $HTTP_SERVER_VARS[устарела]

hobodave
источник
12
использовать $ _SERVER ['SERVER_PORT'] может быть сложно ... например, ispconfig использует порт 81 в качестве безопасного порта, поэтому предположим, что 443 является портом "по умолчанию" для ssl.
Габриэль Соса
@ Габриэль Соса - Да, но предупреждения могут быть рассмотрены в каждом конкретном случае. Ответ @ hobodave будет работать для большинства.
Тим Пост
Обратите внимание, что это не будет работать за обратным прокси. Можно подумать, чтобы проверить HTTP_X_FORWARDED_PROTOили HTTP_X_FORWARDED_SSLтакже.
паоло
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']
Энтони Гиббс,
14

Это также работает, когда $_SERVER['HTTPS']не определено

if( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443 ){
    //enable secure connection
}
Thamaraiselvam
источник
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. С этим на месте и после перезапуска у вас должна быть переменная окружения HTTPS, о которой вы всегда мечтали

томас-питер
источник
5
Лучше быть уверенным, что HTTPS действительно работает; это заставит сервер лгать вам, если это не так.
Брэд Кох
Также вам нужен модуль SetEnv, чтобы это работало. Он включен по умолчанию, но вы никогда не знаете, что может отключить администратор сервера.
toon81
Очень полезно, если вы находитесь на докере через обратный прокси. Спасибо!
Дикирилл
9

Создание моей собственной функции от чтения всех предыдущих сообщений:

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 ... поэтому ему не доверяют.
Фирас Абд Алрахман
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, так как это прямая копия-вставка, которая всегда будет работать. Эта часть может быть использована в производственном сценарии.

Игорь Михайлович - PortalPress.org
источник
3

Я не думаю, что добавление порта - это хорошая идея, особенно если у вас много серверов с разными сборками. это просто добавляет еще одну вещь, которую нужно помнить, чтобы изменить. глядя на документацию, я думаю, что последняя строка кайзеров довольно хороша, так что:

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

кажется достаточно идеально

sp3c1
источник
3

Единственный надежный метод - тот, который описал Игорь М.

$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, он переводится как пустое значение, а не «выкл», не 0, и вы обречены.

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

Гальвани
источник
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*/}
Werezywolf
источник
Это ничего не говорит вам об использовании HTTPS с балансировщиком нагрузки / прокси.
Брэд
3

Кратчайший путь, которым я пользуюсь:

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

Если используется https, то $ secure_connection имеет значение true.

Маркус Зеллер
источник
echo (!empty($_SERVER['HTTPS'])?'https':'http');дает вам httpилиhttps
Хави Эстеве
, сделай это(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie
2

Вы можете проверить, $_SERVER['SERVER_PORT']как SSL обычно работает на порту 443, но это не является надежным.

pix0r
источник
$ _SERVER ['SERVER_PORT'] делает, однако.
Тайлер Картер
2

Что ты думаешь об этом?

if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
    $scheme = 'https';
else
    $scheme = 'http';
тони рмц
источник
Да, есть. Если вы полагаетесь только на empty (), PHP завершится с ошибкой, если нет индекса HTTPS.
toni rmc
3
"empty () по сути является кратким эквивалентом! isset ($ var) || $ var == false" - php.net/manual/en/function.empty.php
Джон Магнолия
2
Ты прав. Забавно, я пропустил это. Я всегда думал, что empty () потерпит неудачу, если переменная не существует.
Тони Rmc
2

На моем сервере (Ubuntu 14.10, Apache 2.4, php 5.5) переменная $_SERVER['HTTPS']не устанавливается, когда скрипт php загружается через https. Я не знаю, что не так. Но следующие строки в .htaccessфайле решают эту проблему:

RewriteEngine on

RewriteCond %{HTTPS} =on [NC] 
RewriteRule .* - [E=HTTPS:on,NE]
Karry
источник
1

Вот функция многократного использования, которую я использовал некоторое время. НТН.

Примечание. Значение 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;
}
crmpicco
источник
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. Chrome начиная с версии 44 не устанавливает заголовок HTTP: 1, поэтому проверка HTTP_HTTPS в порядке. Если этот код не соответствует https, это означает, что ваш веб-сервер или прокси-сервер плохо настроены. Apache сам устанавливает флаг HTTPS правильно, но могут возникнуть проблемы при использовании прокси (например, nginx). Вы должны установить заголовок в виртуальном хосте nginx https

proxy_set_header   X-HTTPS 1;

и использовать модуль Apache для правильной установки флага HTTPS, ища X-HTTPS от прокси. Поиск mod_fakessl, mod_rpaf и т. Д.

mikep
источник
0

Если вы используете балансировщик нагрузки Incapsula, вам нужно использовать IRule для создания настраиваемого заголовка для вашего сервера. Я создал заголовок HTTP_X_FORWARDED_PROTO, который равен либо «http», если порт установлен на 80, и «https», если он равен 443.

Надав
источник
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, и мы должны убедиться, что он установил наш пакет API на сайте http или https).

Вот функция, которую я использую - в основном, просто вызовите URL через cURL, чтобы увидеть, работает ли 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, возможно, вам не нужно менять ..) Подавляющее большинство сайтов такие же, и, вероятно, должны перенаправить вас сами, но эта дополнительная проверка имеет свое применение (конечно, как я уже сказал, в проекте, где пользователь вводит информацию о своем сайте, и вы хотите убедиться на стороне сервера)

Поддержка CFP
источник
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

Я использовал основное предложение здесь и раздражался из-за «Уведомления PHP» в журналах, когда HTTPS не был установлен. Вы можете избежать этого, используя объединяющий нуль оператор "??":

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

(Примечание: недоступно до php v7)

garafajon
источник
-7

Согласно посту hobodave: «Установите непустое значение, если скрипт запрашивался по протоколу HTTPS».

if (!empty($_SERVER['HTTPS']))
{
    $secure_connection = true;
}
user128026
источник
сделай это(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie