PHP и редирект с HTTP на HTTPS в Nginx: корректная связка PHP-FPM и заголовков | AdminWiki

PHP и редирект с HTTP на HTTPS в Nginx: корректная связка PHP-FPM и заголовков

05 сентября 2026 18 мин. чтения
Содержание статьи

Для сайта на PHP-FPM нужны два независимых уровня настройки. HTTP server block на порту 80 должен вернуть постоянный редирект на канонический HTTPS-адрес и завершить обработку запроса. HTTPS server block на порту 443 принимает TLS, обслуживает файлы и передает PHP-запросы в PHP-FPM через FastCGI.

Минимальная схема выглядит так: return 301 https://example.com$request_uri; в HTTP-блоке, location ~ [.]php$ в HTTPS-блоке и согласованные параметры HTTPS, REQUEST_SCHEME, SERVER_PORT, SCRIPT_FILENAME и REQUEST_URI. Если TLS завершается на reverse proxy или балансировщике, локальный Nginx может видеть соединение по HTTP. В этом случае внешний HTTPS нужно передавать через доверенный X-Forwarded-Proto.

Ниже приведена базовая конфигурация для PHP 8.3 и Nginx. Путь к сокету, каталог сайта, имена доменов и пути к сертификатам нужно заменить на значения конкретного сервера. Общие принципы подходят для PHP 8.x и современных версий Nginx. Полная схема развертывания LEMP разобрана в руководстве по Nginx, PHP-FPM и MySQL/MariaDB.

Быстрая схема: HTTP на HTTPS, HTTPS в PHP-FPM

Как проходит запрос от браузера до PHP

  1. Браузер отправляет запрос на http://example.com/path по порту 80.
  2. Nginx выбирает HTTP server block по адресу, порту и заголовку Host.
  3. HTTP-блок возвращает код 301 или 308 с заголовком Location: https://example.com/path. До PHP-FPM этот запрос не доходит.
  4. Браузер повторяет запрос по HTTPS. TLS завершается в Nginx на порту 443.
  5. Для обычного URI Nginx применяет try_files. Если приложение использует front controller, запрос передается в /index.php.
  6. Для PHP-файла срабатывает location ~ [.]php$. Nginx проверяет существование файла и передает параметры FastCGI процессу PHP-FPM.
  7. PHP получает сведения о схеме, порте, хосте и URI через массив $_SERVER. Приложение использует их при построении абсолютных ссылок, проверке защищенного соединения и собственных редиректах.

Nginx принимает решение о сетевом редиректе. PHP-FPM не перенаправляет HTTP-запросы сам по себе и не видит TLS без переданных FastCGI-параметров. Поэтому рабочая настройка состоит из отдельного HTTP-блока, HTTPS-блока и согласованного набора переменных окружения.

Какой результат считать корректным

  • Запрос по HTTP получает один ответ 301 или 308.
  • Заголовок Location содержит выбранный канонический домен и схему https.
  • Путь и query-параметры сохраняются без потерь.
  • Запрос к каноническому HTTPS-адресу не получает повторный редирект.
  • Альтернативный host, например www.example.com, переходит на основной домен одним переходом.
  • PHP видит $_SERVER['HTTPS'] = 'on', $_SERVER['REQUEST_SCHEME'] = 'https' и порт 443.
  • PHP-FPM отвечает без ошибок 404, 500 и 502.

Как настроить редирект с HTTP на HTTPS в Nginx для PHP-FPM

HTTP server block: постоянный редирект на канонический HTTPS

HTTP-блок должен содержать минимум директиву return. Она завершает обработку запроса сразу и не передает его в PHP. Для обычных переходов используйте 301:

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    return 301 https://example.com$request_uri;
}

Переменная $request_uri содержит исходный URI вместе с query-параметрами. Запрос /catalog/item?id=42 перейдет на https://example.com/catalog/item?id=42. Nginx не добавляет второй вопросительный знак и не теряет строку запроса.

Код 301 подходит для GET и HEAD, когда клиенту достаточно изменить адрес. Код 308 сохраняет HTTP-метод и тело запроса:

return 308 https://example.com$request_uri;

Это важно для POST, PUT и других методов, которые нельзя незаметно превращать в GET. Постоянный редирект лучше включать после проверки через 302 в тестовой среде или после подтверждения всей цепочки. Браузеры и поисковые роботы кэшируют ответы 301 и 308, поэтому ошибка в адресе может сохраняться после исправления конфигурации.

Для канонизации домена задавайте адрес явно. Вариант return 301 https://$host$request_uri; сохраняет любой принятый host и может отправить посетителя на нежелательный домен. Такая схема особенно рискованна, если сервер принимает неизвестные заголовки Host в default server block.

Если нужен переход с www на основной домен, настройте отдельный HTTPS-блок для альтернативного имени:

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    return 301 https://example.com$request_uri;
}

Сертификат альтернативного имени должен содержать www.example.com. Иначе TLS-соединение завершится раньше редиректа, и браузер покажет ошибку сертификата. Практические параметры сертификатов и проверку TLS разобраны в руководстве по установке SSL-сертификата на Nginx и Apache.

HTTPS server block: TLS и маршрутизация PHP-запросов

Основной HTTPS-блок обслуживает канонический домен. В примере try_files сначала ищет статический файл, затем отправляет неизвестный URI в front controller. Обработка PHP ограничена существующими файлами, поэтому Nginx не передает в PHP произвольный путь.

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name example.com;

    root /var/www/example.com/public;
    index index.php index.html;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ [.]php$ {
        try_files $uri =404;

        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param REQUEST_SCHEME $scheme;
        fastcgi_param HTTPS on;
        fastcgi_param SERVER_PORT 443;
        fastcgi_param HTTP_HOST $host;
        fastcgi_param SERVER_NAME $server_name;
        fastcgi_param REQUEST_URI $request_uri;

        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
}

В HTTPS server block переменная $scheme равна https, а переменная $server_port обычно равна 443. В примере значения передаются явно, чтобы поведение не зависело от содержимого системного include-файла. В конфигурации с нестандартным портом передавайте фактический внешний порт.

Директива try_files $uri =404; внутри PHP location блокирует попытки передать в PHP несуществующий скрипт. Для CMS с front controller это не мешает работе: внутренний URI сначала меняется на существующий /index.php, после чего запрос проходит проверку.

Убедитесь, что каталог root совпадает с фактическим public-каталогом приложения. Если сайт хранится в /var/www/example.com, а документ-корень указан как /var/www/example.com/public, значение SCRIPT_FILENAME должно указывать на реальный файл внутри public-каталога.

Подключение PHP-FPM через Unix-сокет или TCP

Значение fastcgi_pass должно совпадать с активным способом прослушивания PHP-FPM. Для локального сервера чаще используют Unix-сокет:

fastcgi_pass unix:/run/php/php8.3-fpm.sock;

Путь содержит версию PHP только как пример. На сервере может использоваться php8.2-fpm.sock, другой каталог или собственное имя pool. Проверить доступные сокеты можно командами:

ls -l /run/php/
systemctl status php8.3-fpm
ss -lx | grep php

При TCP-подключении Nginx обращается к адресу и порту, который указан в pool PHP-FPM:

fastcgi_pass 127.0.0.1:9000;

Ошибка 502 Bad Gateway появляется, если процесс PHP-FPM остановлен, Nginx использует устаревший путь к сокету, порт закрыт или права на Unix-сокет не позволяют worker-процессу Nginx подключиться к pool. Для проверки прав сравните пользователя Nginx, владельца сокета и группы доступа. В Ubuntu и Debian worker часто работает от имени www-data, но это значение нужно проверять в конфигурации конкретной системы.

Для тестового стенда с отдельным VDS можно использовать облачную инфраструктуру Timeweb Cloud: на чистом сервере проще воспроизвести конфигурацию, проверить версии PHP и отладить переход на HTTPS до изменения production-узла.

Какие fastcgi_param передать PHP для корректной схемы HTTPS

Параметры HTTPS, REQUEST_SCHEME и SERVER_PORT

PHP получает сведения о веб-запросе через FastCGI. Само подключение Nginx к PHP-FPM по Unix-сокету или TCP не сообщает PHP, что клиент использовал HTTPS. Это состояние передается отдельными параметрами.

ПараметрПример значенияНазначение
HTTPSonПозволяет PHP и CMS определить защищенный запрос через $_SERVER['HTTPS'].
REQUEST_SCHEMEhttpsПередает схему запроса приложениям, которые читают $_SERVER['REQUEST_SCHEME'].
SERVER_PORT443Передает внешний порт для генерации абсолютных URL и проверок конфигурации.
HTTP_HOSTexample.comПередает host, по которому приложение сопоставляет домен и формирует ссылки.

Для Nginx, который сам завершает TLS, базовая связка выглядит так:

fastcgi_param HTTPS on;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param SERVER_PORT $server_port;

В отдельном HTTPS-блоке $scheme равен https, а $server_port равен 443. В PHP результат можно проверить временным диагностическим скриптом:

<?php
header('Content-Type: text/plain; charset=utf-8');
var_dump([
    'HTTPS' => $_SERVER['HTTPS'] ?? null,
    'REQUEST_SCHEME' => $_SERVER['REQUEST_SCHEME'] ?? null,
    'SERVER_PORT' => $_SERVER['SERVER_PORT'] ?? null,
    'HTTP_HOST' => $_SERVER['HTTP_HOST'] ?? null,
    'REQUEST_URI' => $_SERVER['REQUEST_URI'] ?? null,
]);

После проверки удалите диагностический файл или закройте к нему доступ. Публичный вывод переменных окружения раскрывает сведения о сервере и маршрутизации.

Если PHP получает пустой HTTPS, приложение может считать запрос незашифрованным и отправлять новый редирект на тот же HTTPS-адрес. Если REQUEST_SCHEME остается равным http, фреймворк способен генерировать ссылки с неправильной схемой даже при корректном ответе Nginx.

SCRIPT_FILENAME, REQUEST_URI и HTTP_HOST

Параметр SCRIPT_FILENAME определяет файл, который запускает PHP-FPM:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

При URI /index.php и корне /var/www/example.com/public PHP-FPM получает путь /var/www/example.com/public/index.php. Ошибка в этой строке дает 404, ошибку открытия файла или запуск не того скрипта.

REQUEST_URI сохраняет полный исходный путь вместе с query-параметрами. QUERY_STRING передает строку параметров отдельно. Для приложений, которые используют оба значения, передайте их явно:

fastcgi_param REQUEST_URI $request_uri;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;

HTTP_HOST нужен CMS и фреймворкам для выбора виртуального домена. В основном HTTPS-блоке безопаснее передавать нормализованный $host, а канонический домен хранить в настройках приложения:

fastcgi_param HTTP_HOST $host;
fastcgi_param SERVER_NAME $server_name;

Не стройте канонический URL на полном доверии входному заголовку Host. Разрешите конкретные server name, задайте основной домен в CMS или переменную базового URL фреймворка. Для запроса к example.com приложение должно создавать https://example.com/... независимо от случайного или неизвестного host.

Если fastcgi.conf в вашей системе уже содержит SCRIPT_FILENAME, не дублируйте его без проверки. На части дистрибутивов fastcgi_params содержит базовые CGI-параметры, а fastcgi.conf дополнительно задает путь к скрипту. Просмотрите фактическое содержимое include-файлов командой nginx -T и оставьте один источник для каждого параметра.

Почему один fastcgi_param HTTPS не решает все проблемы

Приложение может проверять несколько значений одновременно:

  • $_SERVER['HTTPS'] показывает состояние HTTPS.
  • $_SERVER['REQUEST_SCHEME'] определяет схему URL.
  • $_SERVER['SERVER_PORT'] сообщает порт.
  • $_SERVER['HTTP_HOST'] содержит домен запроса.
  • $_SERVER['HTTP_X_FORWARDED_PROTO'], HTTP_X_FORWARDED_HOST и HTTP_X_FORWARDED_PORT описывают внешний запрос за proxy.
  • Переменная базового URL в CMS или фреймворке может полностью переопределять значения окружения.

Поэтому исправление одной строки fastcgi_param HTTPS on; устраняет лишь одну причину. Если приложение получает HTTPS=on, но REQUEST_SCHEME=http, его middleware может продолжить редирект. Если схема правильная, а host указывает на внутреннее имя балансировщика, абсолютные ссылки приведут пользователя к недоступному адресу.

Для прямого TLS в Nginx используйте единый набор:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS on;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_param HTTP_HOST $host;

Перед вставкой этого блока проверьте include-файл. Дублирование REQUEST_METHOD, QUERY_STRING и других параметров обычно не ломает простой запрос, но усложняет диагностику: итоговое значение зависит от порядка директив и обработки повторяющихся полей FastCGI.

HTTPS за reverse proxy или балансировщиком: как избежать цикла редиректов

Как возникает бесконечный HTTPS-редирект

Типичный цикл появляется при TLS termination на внешнем proxy:

  1. Браузер открывает https://example.com.
  2. Балансировщик принимает TLS и отправляет обычный HTTP-запрос на backend Nginx.
  3. Backend Nginx видит локальный $scheme = http.
  4. Правило HTTP на backend возвращает Location: https://example.com.
  5. Браузер снова подключается к балансировщику по HTTPS и повторяет тот же цикл.

Смена 301 на 302 или 307 не исправляет причину. Код ответа влияет на кэширование и метод запроса, но не меняет значение локального $scheme. Нужно передать backend внешний протокол и настроить приложение на доверие к этому сигналу.

В access log цикл обычно выглядит как серия одинаковых запросов с ответами 301 или 302. Внешний клиент видит повторяющийся URL, а backend каждый раз считает соединение HTTP.

Безопасная обработка X-Forwarded-Proto

На доверенном proxy заголовок нужно перезаписывать, а не передавать значение клиента:

location / {
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_pass http://backend;
}

Если внешний proxy принимает HTTPS, его $scheme равен https. Backend получает это значение в X-Forwarded-Proto, хотя транспорт между двумя узлами идет по HTTP. Внешний proxy должен находиться в закрытой сети или иметь сетевые ограничения, чтобы клиент не обращался к backend напрямую.

В backend Nginx можно нормализовать доверенный заголовок через map. Директивы map размещают в контексте http, не внутри server:

map $http_x_forwarded_proto $external_scheme {
    default http;
    ~*^https$ https;
}

map $external_scheme $external_https {
    default off;
    https on;
}

map $http_x_forwarded_port $external_port {
    default 80;
    80 80;
    443 443;
}

Значение default http означает, что неизвестная схема не получает статус HTTPS. Регулярное правило принимает только точное значение https, а строку вроде https,http оставляет в состоянии http. Такая нормализация не создает доверие сама по себе. Доступ к backend нужно ограничить адресами proxy на уровне firewall или сетевой политики.

В PHP location backend передавайте внешние значения:

fastcgi_param REQUEST_SCHEME $external_scheme;
fastcgi_param HTTPS $external_https;
fastcgi_param SERVER_PORT $external_port;
fastcgi_param HTTP_HOST $host;
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
fastcgi_param HTTP_X_FORWARDED_HOST $http_x_forwarded_host;
fastcgi_param HTTP_X_FORWARDED_PORT $http_x_forwarded_port;

Последние три параметра передавайте приложению только при доверенной цепочке proxy. Если backend доступен из интернета, любой клиент сможет прислать собственный X-Forwarded-Proto и повлиять на генерацию URL или логику редиректа. Настройка trusted proxy в CMS или фреймворке должна соответствовать реальным адресам балансировщиков.

Где размещать редирект при TLS termination

Есть две устойчивые схемы.

СхемаГде выполняется редиректЧто получает backend
Редирект на edgeБалансировщик или внешний Nginx перенаправляет HTTP на HTTPS.До backend доходят только HTTPS-запросы с нормализованными proxy-заголовками.
Редирект на backendBackend принимает запросы обоих типов и проверяет доверенный X-Forwarded-Proto.PHP и Nginx получают внешнюю схему, host и порт.

Редирект на edge обычно проще: внешний уровень видит реальную схему клиента и не создает лишний переход между proxy и backend. В этом случае backend не должен безусловно проверять свой локальный $scheme и отправлять HTTPS-запросы на HTTPS повторно.

Если редирект должен выполнять backend, условие нужно строить на нормализованной переменной:

if ($external_scheme = http) {
    return 301 https://example.com$request_uri;
}

В Nginx конструкция if с единственной директивой return безопасна для такого сценария. Не смешивайте это правило с параллельным редиректом на балансировщике, иначе диагностика цепочки станет сложнее.

X-Forwarded-Host и X-Forwarded-Port нужны, когда приложение строит абсолютные URL, ссылки на callback или адреса API. Их должен задавать внешний proxy. Если публичный адрес всегда один, надежнее закрепить домен и порт в настройках приложения и использовать proxy-заголовки только после проверки доверенного источника. Практические схемы SSL termination, маршрутизации и поиска ошибок 502 разобраны в руководстве по reverse proxy на Nginx.

Как быстро проверить редирект в Nginx через curl

Минимальная команда curl -I для проверки заголовков

Начните с HTTP-ответа без загрузки тела:

curl -I http://example.com/path?x=1

Ожидаемый ответ содержит 301 или 308 и примерно такой заголовок:

HTTP/1.1 301 Moved Permanently
Location: https://example.com/path?x=1

Проверьте три значения: код ответа, схему и host в Location, сохранение пути и query-параметра x=1. Для просмотра заголовков без тела подойдет команда:

curl -sS -D - -o /dev/null http://example.com/path?x=1

curl -I отправляет HEAD. Некоторые приложения обрабатывают HEAD иначе, чем GET, поэтому после проверки заголовков выполните обычный запрос.

Как проверить цепочку через curl -L и обнаружить петлю

Команда с ограничением числа переходов показывает промежуточные ответы:

curl -IL --max-redirs 10 --silent --show-error http://example.com/path?x=1

При исправной схеме обычно видны два ответа: первый HTTP с 301 или 308, второй HTTPS с кодом приложения, например 200. Повтор одного и того же Location указывает на цикл. Повторение переходов между двумя host часто означает конфликт правил www и non-www.

Для итогового адреса и количества переходов используйте форматированный вывод:

curl -sS -o /dev/null -w 'final=%{url_effective} code=%{http_code} redirects=%{num_redirects}' -L http://example.com/path?x=1

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

Как проверить разные сценарии запроса

  • HTTP основного домена: curl -I http://example.com/.
  • HTTPS основного домена: curl -I https://example.com/.
  • HTTP альтернативного домена: curl -I http://www.example.com/.
  • Путь без завершающего слеша: curl -I http://example.com/docs.
  • Путь с завершающим слешем: curl -I http://example.com/docs/.
  • Query-параметры: curl -I 'http://example.com/search?q=nginx&page=2'.
  • Обычный GET: curl -sS -D - -o /dev/null http://example.com/.
  • POST для проверки сохранения метода при 308: curl -sS -D - -o /dev/null -X POST --data 'a=1' http://example.com/form.

До изменения DNS можно направить домен на конкретный IP через --resolve:

curl --resolve example.com:443:203.0.113.10 -I https://example.com/path
curl --resolve example.com:80:203.0.113.10 -I http://example.com/path

Эта проверка сохраняет исходный host и имя для TLS, но подключается к указанному адресу. Она помогает тестировать новый сервер до переключения DNS.

Каким должен быть корректный ответ

Настройка прошла проверку, если выполняются все условия:

  • HTTP дает один переход на HTTPS.
  • Альтернативный host дает один переход на канонический домен.
  • Location не содержит внутреннего имени, лишнего порта или двойного слеша.
  • Путь и query-параметры совпадают с исходным запросом.
  • HTTPS отвечает ожидаемым кодом приложения, чаще 200, 204 или кодом авторизации.
  • В HTTPS-ответе нет второго Location, если приложение не использует отдельную штатную переадресацию.
  • PHP-запрос доходит до рабочего pool PHP-FPM.
  • В Nginx error log и PHP-FPM log не появились новые ошибки.

Проверка только кода 301 недостаточна. Неправильный домен, потерянные параметры и второй редирект обнаруживаются при просмотре Location и полной цепочки.

Типовые ошибки конфигурации Nginx и диагностика по логам

Ищите неисправность по слоям: выбранный server block, правило маршрутизации, FastCGI-параметры, доступность PHP-FPM, логика приложения. Такая последовательность сокращает число случайных изменений.

СимптомВероятная причинаПроверкаИсправление
HTTP отвечает 200Выбран другой server block, правило return отсутствует или запрос попал в default server.nginx -T, заголовок Host, поле server_name в access log.Активируйте нужный конфиг, уберите конфликтующие server blocks и задайте корректный listen.
Редирект ведет не на тот hostИспользуется $host без канонизации, неверно задан server_name или proxy передает внутренний host.curl -I, итоговый конфиг, X-Forwarded-Host.Укажите канонический домен явно и настройте доверенную передачу host от proxy.
Пропадают параметры запросаВ rewrite или return указан неправильный URI, приложение пересобирает URL без query string.Проверка URI с ?x=1, заголовок Location.Используйте $request_uri для полного исходного URI.
Бесконечный HTTPS-циклBackend видит HTTP после TLS termination и игнорирует доверенный X-Forwarded-Proto.curl -IL, access log proxy и backend, значения scheme, HTTPS, REQUEST_SCHEME.Оставьте редирект на edge или нормализуйте внешний протокол на backend.
HTTPS отвечает 404Неверный root, ошибка try_files или неправильный SCRIPT_FILENAME.Проверка файла на диске, nginx -T, error log.Исправьте public-каталог, front controller и путь к PHP-файлу.
HTTPS отвечает 500Ошибка PHP-кода, несовместимость версии или исключение приложения.PHP-FPM log, журнал приложения, прямой тест существующего PHP-файла.Исправьте код или зависимости, проверьте версию PHP и настройки pool.
HTTPS отвечает 502PHP-FPM остановлен, сокет отсутствует, порт неверен или нет прав на подключение.systemctl status, ls -l /run/php/, Nginx error log.Запустите нужный pool и синхронизируйте fastcgi_pass с его listen-адресом.

Location указывает не на тот адрес

Проверьте порядок и состав виртуальных хостов. Nginx выбирает server block по адресу и порту, затем сопоставляет server_name. Если домен не совпал, запрос может попасть в default server и получить чужой сертификат, страницу или редирект.

В HTTP-блоке проверьте:

  • порт 80 действительно прослушивается;
  • основной и альтернативный host перечислены в server_name;
  • в return указан канонический домен;
  • используется $request_uri, если нужно сохранить путь и параметры;
  • нет второго правила, которое меняет host или добавляет путь.

Правила rewrite с регулярными выражениями часто создают лишние переходы и теряют query string. Для полного перехода домена используйте короткий return. Разбор таких правил приведен в инструкции по редиректам в Nginx без rewrite.

Редирект зациклился

Сначала определите, кто возвращает ответ. Выполните:

curl -IL --max-redirs 10 --silent --show-error https://example.com/

Затем сопоставьте каждый ответ со временем и URI в access log. Если ответ формирует edge, проверяйте правила балансировщика. Если ответ формирует backend Nginx, сравнивайте локальный $scheme с заголовком X-Forwarded-Proto. Если Nginx возвращает код 200, а затем редирект появляется в цепочке, источник находится в PHP-приложении.

В PHP проверьте значения:

$_SERVER['HTTPS']
$_SERVER['REQUEST_SCHEME']
$_SERVER['SERVER_PORT']
$_SERVER['HTTP_HOST']
$_SERVER['HTTP_X_FORWARDED_PROTO']
$_SERVER['HTTP_X_FORWARDED_HOST']

CMS и фреймворки часто требуют отдельной настройки trusted proxies. Без нее приложение может игнорировать корректный proxy-заголовок или, наоборот, принимать подмененное значение от клиента.

Проверьте, не настроен ли один и тот же переход сразу на CDN, балансировщике, Nginx и в CMS. Для каждой пары исходная схема и целевая схема должны быть определены один раз.

Вместо редиректа приходит 200, 404, 500 или 502

200 на HTTP. Запрос обслуживает неправильный server block или HTTP-редирект отсутствует. Проверьте активный конфигурационный файл и тестируйте запрос с явным Host, если DNS указывает на несколько узлов.

404 на HTTPS. Nginx не нашел статический файл или PHP-скрипт. Сравните root с реальным каталогом, проверьте существование index.php и значение SCRIPT_FILENAME. В front controller запрос должен доходить до существующего файла.

500 на HTTPS. Nginx уже передал запрос в PHP-FPM, поэтому ищите ошибку в PHP-коде, зависимостях, правах приложения или конфигурации pool. Код 500 не исправляется сменой fastcgi_pass, если соединение с PHP-FPM уже установлено.

502 на HTTPS. Nginx не получил корректный ответ от PHP-FPM. Сверьте версию сокета, состояние службы и права доступа. Для TCP проверьте, слушает ли процесс нужный адрес:

ss -ltnp | grep 9000
systemctl status php8.3-fpm
journalctl -u php8.3-fpm -n 100 --no-pager

Логи Nginx как подтверждение причины

До изменения конфигурации проверьте синтаксис и итоговый набор директив:

nginx -t
nginx -T

nginx -t проверяет синтаксис и доступность ссылок на конфигурацию. nginx -T выводит собранную конфигурацию вместе с include-файлами. Смотрите именно этот результат, потому что отдельный файл в каталоге sites-available не подтверждает его подключение.

Для временной диагностики можно добавить отдельный формат access log в контекст http:

log_format redirect_debug '$remote_addr request=$request status=$status host=$host scheme=$scheme port=$server_port uri=$request_uri location=$sent_http_location upstream=$upstream_status';

Подключите его к нужному server block или location, затем выполните reload:

nginx -t && systemctl reload nginx

В логах сопоставляйте status, host, scheme, port, uri и location. Не записывайте полные заголовки и тела запросов без необходимости: query string иногда содержит токены, идентификаторы и персональные данные.

В error log ищите ошибки выбора upstream, доступа к сокету, открытия PHP-файла и обработки правил. В PHP-FPM log ищите фатальные ошибки, аварийное завершение worker и сообщения о правах. Такая связка логов показывает, на каком переходе запрос перестал соответствовать ожидаемой схеме.

Итоговый чек-лист проверки редиректа и PHP-FPM

Проверка конфигурации и применения изменений

  1. Проверьте синтаксис командой nginx -t.
  2. Просмотрите итоговую конфигурацию через nginx -T.
  3. Убедитесь, что активен нужный файл виртуального хоста.
  4. Проверьте listen 80, listen 443 ssl и значения server_name.
  5. Проверьте сертификат, приватный ключ и наличие нужных доменных имен в сертификате.
  6. Сверьте root с каталогом сайта.
  7. Проверьте существование PHP-FPM-сокета или доступность TCP-порта.
  8. Сверьте пользователя Nginx с правами на Unix-сокет и файлы приложения.
  9. После успешной проверки выполните systemctl reload nginx.
  10. Проверьте статус службы PHP-FPM и отсутствие новых ошибок в журналах.

Проверка канонического URL и прокси-заголовков

  • HTTP основного домена переходит на канонический HTTPS.
  • HTTP альтернативного домена переходит на HTTPS основного домена.
  • HTTPS основного домена не получает лишний редирект.
  • Путь, завершающий слеш и query-параметры сохраняются.
  • Location не использует внутренний host или адрес backend.
  • За reverse proxy внешний источник перезаписывает X-Forwarded-Proto, X-Forwarded-Host и X-Forwarded-Port.
  • Backend принимает эти заголовки только от разрешенных proxy.
  • Значения REQUEST_SCHEME, HTTPS и SERVER_PORT совпадают с внешней схемой.
  • Настройки trusted proxy в PHP-приложении соответствуют реальной топологии.

Что считать проверкой, завершенной успешно

Настройка готова, когда HTTP-запрос дает один переход на канонический HTTPS-адрес, URI и параметры остаются прежними, HTTPS возвращает ожидаемый код приложения, а PHP-FPM обрабатывает запрос без ошибок. За proxy backend должен видеть внешнюю схему через доверенный и нормализованный контекст.

Постоянный код 301 или 308 включайте после проверки тестовыми запросами. HSTS добавляйте после полной проверки HTTPS и всех нужных поддоменов: браузер начнет автоматически заменять HTTP на HTTPS и усложнит откат ошибочной настройки.

Для повторной проверки после переноса сайта или смены балансировщика используйте один набор тестов: curl -I для заголовков, curl -IL для цепочки, curl -L -w для конечного URL, nginx -t для синтаксиса и журналы Nginx и PHP-FPM для подтверждения причины.

Поделиться:
Сохранить гайд? В закладки браузера