После переноса WordPress за Cloudflare, Nginx Proxy, балансировщик или другой reverse proxy сайт может начать бесконечно перенаправлять HTTP на HTTPS. В браузере это часто проявляется ошибкой «слишком много перенаправлений», хотя SSL-сертификат на прокси установлен правильно.
Типичная причина — разные уровни по-разному определяют протокол. Пользователь подключается к прокси через HTTPS, но прокси обращается к PHP-серверу по обычному HTTP. WordPress видит HTTP, пытается перенаправить запрос на HTTPS, а прокси снова передаёт его на бэкенд как HTTP. Ниже разберём последовательность проверок и безопасное исправление.
Почему возникает redirect loop
В схеме с reverse proxy есть как минимум два отдельных соединения: браузер ↔ прокси и прокси ↔ сервер WordPress. HTTPS может завершаться на Cloudflare, Nginx или load balancer, тогда как между прокси и PHP-FPM используется HTTP.
PHP обычно определяет защищённое соединение по переменной `$_SERVER['HTTPS']` или через функцию WordPress `is_ssl()`. Если прокси не передал информацию об исходном протоколе, бэкенд считает запрос HTTP. WordPress, настроенный на адрес `https://example.com`, отправляет ответ 301 или 302 с перенаправлением на HTTPS. Следующий запрос проходит по тому же маршруту и снова определяется как HTTP.
Важно отличать эту проблему от ошибок DNS, неправильного сертификата или цикла между двумя правилами редиректа. Сначала нужно установить, действительно ли прокси передаёт протокол и учитывает ли его WordPress.
- Клиент открывает сайт через HTTPS.
- Reverse proxy принимает TLS-соединение и часто передаёт запрос к origin по HTTP.
- PHP не получает или не учитывает `X-Forwarded-Proto: https`.
- WordPress видит HTTP и повторно перенаправляет запрос на HTTPS.
Как диагностировать проблему до внесения изменений
Проверьте цепочку редиректов из командной строки. Команда покажет HTTP-заголовки и адрес каждого перехода:
curl -I -L --max-redirs 10 https://example.com
Что искать в ответе
Если один и тот же URL повторяется в нескольких ответах `Location`, а количество переходов растёт, вероятен цикл. Обратите внимание, меняется ли схема с `http://` на `https://`, добавляются или удаляются `www`, слеши и другие части адреса.
Временно проверьте значение протокола на сервере через защищённый диагностический endpoint или WP-CLI. Не оставляйте такой endpoint доступным публично: он может раскрывать служебные переменные и конфигурацию.
- `is_ssl()` должен возвращать `true` при запросе через HTTPS.
- `$_SERVER['HTTPS']` должен быть установлен или иметь значение `on` либо эквивалентное значение для вашего стека.
- `HTTP_X_FORWARDED_PROTO` должен содержать `https`, если именно прокси завершает TLS.
<?php
require_once __DIR__ . '/wp-load.php';
header('Content-Type: text/plain; charset=utf-8');
var_dump(is_ssl());
var_dump($_SERVER['HTTPS'] ?? null);
var_dump($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? null);
Настройте передачу протокола на reverse proxy
Прокси должен передавать бэкенду протокол, который использовал клиент. Наиболее распространённый вариант — заголовок `X-Forwarded-Proto: https`. При этом важно, чтобы прокси самостоятельно перезаписывал этот заголовок, а не просто принимал его от клиента.
Для Nginx значения обычно передаются в блоке проксирования. Конкретные параметры зависят от вашей схемы, но принцип выглядит так:
- Передавайте `Host`, чтобы WordPress формировал правильные абсолютные URL.
- Передавайте реальный IP-адрес клиента в отдельных заголовках, если это требуется для логов или безопасности.
- Для HTTPS-запросов устанавливайте `X-Forwarded-Proto` в значение `https`, а не доверяйте значению, пришедшему из Интернета.
- На origin-сервере ограничьте доступ так, чтобы служебные заголовки можно было получить только от доверенного прокси.
location / {
proxy_pass http://wordpress_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Cloudflare и load balancer
Cloudflare обычно передаёт информацию о схеме через `X-Forwarded-Proto` или собственные служебные заголовки. В конфигурации load balancer название заголовка и способ его формирования могут отличаться. Проверьте фактический HTTP-запрос на origin, а не полагайтесь только на настройки панели.
Если перед WordPress установлено несколько прокси, определите, какой из них является доверенным источником информации о протоколе. Цепочка вроде `http, https` может потребовать отдельного разбора, а не простого сравнения всего значения со строкой `https`.
Сообщите WordPress, что запрос был выполнен по HTTPS
Если веб-сервер не устанавливает `$_SERVER['HTTPS']` автоматически, добавьте совместимую проверку в `wp-config.php`. Код должен располагаться до строки `/* That's all, stop editing! Happy publishing. */` или её аналога.
if (
isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https'
) {
$_SERVER['HTTPS'] = 'on';
}
Безопасное использование заголовка
Не добавляйте эту логику без проверки доверенной инфраструктуры. Клиент может попытаться самостоятельно отправить `X-Forwarded-Proto: https`. Если origin-сервер доступен напрямую и без фильтрации принимает этот заголовок, злоумышленник сможет влиять на определение схемы.
Более надёжный подход — разрешить доступ к origin только с адресов reverse proxy, перезаписывать заголовок на прокси и отбрасывать внешнее значение. Если в вашей инфраструктуре заголовок может содержать список значений, сначала согласуйте формат с администратором прокси, а не копируйте простую проверку без изменений.
- Не используйте одновременно несколько противоречивых фрагментов для определения HTTPS.
- Не устанавливайте `$_SERVER['HTTPS'] = 'on'` для всех запросов, если бэкенд обслуживает и настоящий HTTP.
- После редактирования проверьте синтаксис PHP и перезапустите нужные сервисы, если этого требует ваш стек.
Проверьте URL WordPress, кеш и cookies
В базе данных значения `home` и `siteurl` должны использовать ту же каноническую схему и домен, которые видит пользователь, например `https://example.com`. Если одно из значений осталось на `http://`, редиректы могут продолжиться даже после исправления reverse proxy.
Проверить адреса можно с помощью WP-CLI:
wp option get home
wp option get siteurl
Что очистить после изменения
Очистите page cache WordPress, кеш веб-сервера, CDN-кеш и кеш reverse proxy, если он используется. Старый 301 может сохраняться в браузере, поэтому тестируйте в приватном окне или через `curl`.
Также проверьте домен cookie, правила HTTP-аутентификации и плагины, которые принудительно включают HTTPS. Два разных механизма редиректа — например, правило на прокси и плагин в WordPress — могут создавать цикл или непредсказуемую последовательность переходов.
- Канонический адрес сайта должен быть один: HTTPS и нужный вариант домена.
- Правила для `www` и домена без `www` не должны перенаправлять друг на друга.
- После тестирования временный диагностический код необходимо удалить.
Типичные ошибки и финальная проверка
Чаще всего проблема сохраняется из-за изменения только одного уровня. Например, в `wp-config.php` добавили проверку заголовка, но Nginx не передаёт этот заголовок; или Cloudflare работает корректно, но origin доступен напрямую и получает поддельные служебные значения.
Проверяйте сайт в нескольких сценариях: главная страница, страница записи, админпанель, запрос без кеша и запрос через CDN. Для каждого сценария должна использоваться одна схема HTTPS, а повторные переходы должны отсутствовать.
- Просмотрите цепочку через `curl -I -L` и убедитесь, что она завершается кодом 200, а не повторяющимся 301 или 302.
- Проверьте `is_ssl()` именно через публичный путь, которым пользуются посетители.
- Убедитесь, что `home` и `siteurl` совпадают с каноническим HTTPS-адресом.
- Проверьте, нет ли дублирующего редиректа в Nginx, Cloudflare, балансировщике, WordPress или плагине.
- Ограничьте доступ к origin и не доверяйте `X-Forwarded-Proto` от произвольного клиента.
- После исправления очистите все уровни кеша и повторите проверку.
Частые вопросы
Нужно ли всегда добавлять код в wp-config.php?
Нет. Если веб-сервер или reverse proxy корректно устанавливает `$_SERVER['HTTPS']`, дополнительный код не нужен. Сначала проверьте `is_ssl()` и фактические заголовки запроса.
Можно ли просто принудительно установить `$_SERVER['HTTPS'] = 'on'` для всех запросов?
Это опасно, если origin может обслуживать HTTP или получать некорректные запросы. Лучше устанавливать значение только после проверки заголовка от доверенного прокси.
Почему redirect loop остался после изменения wp-config.php?
Причиной могут быть кеш 301 в браузере или CDN, неправильные `home` и `siteurl`, дублирующее правило редиректа, некорректный заголовок на прокси или доступ к origin в обход нужного маршрута.
Нужно ли менять адрес WordPress в базе данных?
Нужно проверить, что `home` и `siteurl` используют правильный канонический адрес с `https://`. Изменяйте их осторожно и учитывайте сериализованные данные, если выполняете массовую замену в базе.
Вывод
Redirect loop в WordPress за reverse proxy обычно возникает не из-за самого SSL, а из-за потери информации об исходном протоколе между прокси и PHP. Исправление состоит из трёх частей: прокси должен корректно передать HTTPS, WordPress — безопасно учесть этот сигнал, а URL сайта и кеш — соответствовать каноническому HTTPS-адресу.
После изменений проверьте реальную цепочку запроса, значение `is_ssl()`, настройки `home` и `siteurl`, а также защиту origin от поддельных forwarded-заголовков. Это позволит устранить цикл без случайного отключения HTTPS или создания новых проблем с безопасностью.