Принудительное перенаправление на защищенный протокол через PHP

Раздел: Администрирование PHP -> Веб-сервер

Варианты реализации редиректа на HTTPS в PHP

Самый надёжный и часто используемый способ - проверка переменной окружения $_SERVER['HTTPS'] в начале обработки запроса. Если значение не равно 'on' (или отсутствует), скрипт отправляет заголовок Location с кодом 301 (постоянное перенаправление) на HTTPS-версию текущего адреса. Решение подходит для любых веб-серверов, работающих с PHP, и не требует дополнительных настроек.


if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') {
    $redirectUrl = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    header('HTTP/1.1 301 Moved Permanently');
    header('Location: ' . $redirectUrl);
    exit;
}

Этот код следует разместить в самом начале главного исполняемого файла (например, index.php) или в общем подключаемом файле конфигурации. Он обрабатывает все входящие HTTP-запросы, перенаправляя их на защищённый протокол. Важно вызвать exit после отправки заголовков, чтобы скрипт не продолжал выполнение.

Как выполнить перенаправление, опираясь на номер порта (80 для HTTP, 443 для HTTPS)?

Можно проверить $_SERVER['SERVER_PORT']. Если порт равен 80, выполняется редирект. Этот способ удобен, когда HTTPS-порт отличается от стандартного 443.


if ($_SERVER['SERVER_PORT'] == 80) {
    header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
    exit;
}

Недостаток: порт может быть переопределён прокси-сервером, поэтому в продакшене лучше комбинировать с проверкой заголовка X-Forwarded-Proto.

Когда необходимо перенаправлять только определённые страницы (например, /admin)?

Добавляется дополнительная проверка пути или регулярное выражение. Это уменьшает нагрузку на сервер и не вынуждает пользователя переходить на HTTPS там, где это не требуется.


if (empty($_SERVER['HTTPS']) && strpos($_SERVER['REQUEST_URI'], '/admin') === 0) {
    header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
    exit;
}

Как реализовать редирект через JavaScript или мета-тег, если PHP не может быть изменён?

Иногда доступ к серверному коду ограничен. Тогда в HTML-шаблон добавляется скрипт или тег , выполняющий переброс на HTTPS. Такой метод ненадёжен (пользователь может отключить JavaScript) и рассматривается как временное решение.


<script>
if (window.location.protocol !== 'https:') {
    window.location.href = 'https://' + window.location.host + window.location.pathname;
}
</script>

Типичные ошибки и способы их устранения

  • Бесконечный цикл редиректа. Возникает, если условие срабатывает даже при уже работающем HTTPS. Проверьте, что переменная $_SERVER['HTTPS'] действительно принимает значение 'on' при защищённом соединении. Иногда требуется учитывать заголовки от обратного прокси (например, HTTP_X_FORWARDED_PROTO).
  • Потеря POST-данных при редиректе. Код 301 и 302 по умолчанию преобразуют POST-запрос в GET. Для сохранения метода используйте код 307 (временное перенаправление) или 308 (постоянное).
  • Редирект на неправильный домен. Убедитесь, что $_SERVER['HTTP_HOST'] содержит корректный домен, особенно при использовании виртуальных хостов.
  • Заголовки уже отправлены. Если код редиректа размещён после вывода HTML, появится ошибка. Редирект следует выполнять до любой отправки данных (функции echo, print и т.д.).

Расширенные примеры и нестандартные ситуации

Ниже приведены более сложные реализации редиректа на HTTPS с учётом особенностей окружения.

Пример 1. Учёт балансировщика нагрузки (заголовок X-Forwarded-Proto)

Когда запрос проходит через обратный прокси (nginx, HAProxy), реальный протокол передаётся в заголовке HTTP_X_FORWARDED_PROTO. PHP должен проверять и его.

Пример

$isHttps = false;
if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
    $isHttps = true;
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $isHttps = true;
}
if (!$isHttps) {
    $redirectUrl = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    header('HTTP/1.1 301 Moved Permanently');
    header('Location: ' . $redirectUrl);
    exit;
}

Результат:

При запросе на http://example.com/test?foo=bar сервер возвращает заголовки:
HTTP/1.1 301 Moved Permanently
Location: https://example.com/test?foo=bar

Пример 2. Принудительное перенаправление только для административной панели

Селективный редирект снижает нагрузку на SSL-терминацию. В данном примере проверяется начало URI.

Пример

$uri = $_SERVER['REQUEST_URI'];
$protectedPaths = ['/admin', '/account', '/checkout'];
foreach ($protectedPaths as $path) {
    if (strpos($uri, $path) === 0) {
        if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') {
            header('Location: https://' . $_SERVER['HTTP_HOST'] . $uri);
            exit;
        }
        break;
    }
}

Результат:

Пользователь заходит на http://example.com/admin/dashboard -> перенаправляется на https://example.com/admin/dashboard.
Обычная страница http://example.com/news остаётся без редиректа.

Пример 3. Использование HTTP-кода 308 для сохранения метода запроса

Если на HTTPS отправляется форма методом POST, редирект 301/302 потеряет данные. Код 308 гарантирует, что браузер повторно отправит POST-запрос на целевой URL.

Пример

if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') {
    $redirectUrl = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    header('HTTP/1.1 308 Permanent Redirect');
    header('Location: ' . $redirectUrl);
    exit;
}

Результат:

POST http://example.com/login
Ответ: HTTP/1.1 308 Permanent Redirect
Location: https://example.com/login
Браузер повторяет POST-запрос уже на HTTPS.

Пример 4. Комбинирование редиректа и заголовка HSTS

После перенаправления на HTTPS можно отправить заголовок Strict-Transport-Security, который запрещает браузеру использовать HTTP в будущем. Редирект выполняется только для первого визита.

Пример

$isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on');
if (!$isHttps) {
    $redirectUrl = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    header('HTTP/1.1 301 Moved Permanently');
    header('Location: ' . $redirectUrl);
    exit;
}
// Уже HTTPS
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');

Результат:

При первом HTTP-запросе происходит редирект на HTTPS, в ответе появляется заголовок HSTS.
Последующие запросы браузер автоматически направляет на HTTPS без участия сервера.

Редирект на HTTPS в PHP - comments

En
Redirect php https (php)