Після перенесення WordPress за Cloudflare, Nginx Proxy, балансувальник або інший reverse proxy сайт може почати нескінченно перенаправляти HTTP на HTTPS. У браузері це часто проявляється помилкою «too many redirects», хоча 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 або створення нових проблем із безпекою.