Как быстро проверить редирект в Nginx
Корректный редирект в Nginx подтверждают три признака: ожидаемый HTTP-код, правильное значение заголовка Location и конечный URL без циклов. Для быстрой проверки одного ответа используйте curl -I, а для прохождения всей цепочки добавьте -L.
curl -I https://example.com/old
curl -IL https://example.com/old
Первая команда отправляет HEAD-запрос и показывает заголовки исходного ответа. Вторая команда последовательно переходит по заголовкам Location и выводит заголовки всех ответов в цепочке. В первую очередь проверьте строку с кодом, значение Location и число переходов.
Проверяемый инвариант для каждого тестового URL выглядит так: исходный адрес получает ожидаемый код 301, 302, 307 или 308, заголовок Location ведет к нужному адресу, цепочка не повторяется, а конечный URL возвращает ожидаемый результат.
Минимальная команда curl -I для проверки заголовков
Команда curl -I URL запрашивает только заголовки ответа. Она экономит время, когда тело страницы не нужно загружать.
curl -I https://example.com/old
Пример результата:
HTTP/2 301
server: nginx
date: Sat, 05 Sep 2026 10:00:00 GMT
location: https://example.com/new
content-length: 0
Строка HTTP/2 301 показывает версию протокола и код ответа. В другой конфигурации вместо нее может быть HTTP/1.1 301 Moved Permanently. Заголовок server: nginx подтверждает ответ Nginx, но сам по себе не доказывает, что сработало нужное правило. Главный результат проверки находится в строке location.
У curl -I есть ограничение: он отправляет метод HEAD. Большинство веб-серверов обрабатывает HEAD так же, как GET, но приложение, прокси или отдельный обработчик может отвечать иначе. Если результат выглядит неожиданно, сравните его с обычным GET:
curl -i https://example.com/old
Ключ -i добавляет заголовки к ответу GET. Тело страницы при этом будет выведено в терминал, поэтому для больших ответов удобнее использовать вариант с сохранением заголовков и удалением тела:
curl -sS -D - -o /dev/null https://example.com/old
Каким должен быть корректный ответ
Код ответа выбирают по смыслу перенаправления:
| Код | Назначение | Что проверить |
|---|---|---|
| 301 | Постоянный перенос ресурса | Адрес больше не должен возвращаться к старому URL |
| 302 | Временное перенаправление | Старый URL должен продолжать обслуживаться по исходному сценарию после отмены правила |
| 307 | Временное перенаправление с сохранением метода и тела запроса | POST, PUT или другой метод не должен превращаться в GET при переходе |
| 308 | Постоянное перенаправление с сохранением метода и тела запроса | Целевой сервер должен получать тот же метод и данные |
Для обычного переноса страницы чаще используют 301. Временный сценарий обычно проверяют с кодом 302. Для API, загрузки файлов и форм, где сохранение метода имеет значение, проверяйте 307 или 308.
Значение Location должно совпадать с ожидаемой целью по четырем параметрам: схема, домен, порт и путь. Для URL с параметрами добавьте к проверке query string. Например, если запрос был https://example.com/old?page=2, выясните, должен ли целевой адрес сохранить page=2.
HTTP/2 301
location: https://example.com/new?page=2
Ответ 200 вместо ожидаемого редиректа означает, что запрос обработал контент или приложение. Правило перенаправления не сработало либо запрос попал в другой server или location. Код 404 указывает на отсутствие ресурса или неверный маршрут, а 500 и 502 требуют проверки приложения, upstream и журналов.
Как проверить редирект через curl -L и увидеть цепочку
Ключ -L заставляет curl переходить по каждому полученному Location. Один вызов с -L помогает узнать конечный результат, но для диагностики нужно сохранить промежуточные заголовки.
Показать все промежуточные ответы
Для цепочки редиректов используйте curl -IL:
curl -IL https://example.com/old
Условный вывод может выглядеть так:
HTTP/2 301
location: https://example.com/old
HTTP/2 301
location: https://www.example.com/old
HTTP/2 200
content-type: text/html
В этом примере запрос проходит два перенаправления: сначала меняется схема, затем домен. Такая цепочка может работать, но лишние переходы увеличивают время ответа и усложняют диагностику. Когда конечный адрес известен заранее, правило часто можно настроить так, чтобы HTTP-запрос сразу переходил на итоговый HTTPS-адрес и нужный домен.
Для GET-проверки с телом, которое не нужно показывать, применяйте -D - и -o /dev/null:
curl -sS -D - -o /dev/null -L https://example.com/old
В таком выводе будут видны заголовки каждого ответа, а содержимое страниц не попадет в терминал. Этот вариант удобен для скриптов и повторных проверок после изменения конфигурации.
Ограничить число переходов и обнаружить петлю
Лимит переходов защищает проверку от бесконечного обмена между двумя URL:
curl -IL --max-redirs 5 https://example.com/old
curl -v -L --max-redirs 5 https://example.com/old
Если URL повторяется, например /old ведет на /new, а /new снова ведет на /old, curl завершит запрос после установленного лимита и сообщит о превышении числа перенаправлений. Повторяющиеся значения Location показывают петлю напрямую.
Для детального просмотра запроса и ответа используйте:
curl --trace-ascii /dev/stderr -L --max-redirs 5 https://example.com/old
Трассировка показывает отправленный метод, Host, путь, полученный код и заголовки. В ней могут оказаться cookies, токены и другие чувствительные данные, поэтому не публикуйте такой вывод без очистки.
Частые циклы возникают при переключении HTTP и HTTPS, нормализации www, добавлении завершающего слеша и работе приложения за reverse proxy. Для поиска причины выпишите все пары исходный URL, код и Location, затем найдите правило, которое снова обрабатывает уже нормализованный адрес.
Проверить конечный URL без лишнего шума
Для проверки конечного кода, итогового адреса и числа переходов используйте форматированный вывод:
curl -Ls -o /dev/null -w '%{http_code} %{url_effective} %{num_redirects}' https://example.com/old
Пример результата:
200 https://example.com/new 1
Здесь конечный сервер вернул 200, curl прошел один редирект, а итоговым адресом стал https://example.com/new. В автоматической проверке сравнивайте каждое значение с ожидаемым.
Если нужно проверить только исходный ответ и увидеть URL следующего перехода, отключите -L и используйте %{redirect_url}:
curl -sS -o /dev/null -w '%{http_code} %{redirect_url}' https://example.com/old
Такой вызов удобен для проверки одного правила в shell-скрипте. Для полной цепочки добавляйте -L и %{num_redirects}.
Как проверить разные сценарии запроса
Один успешный вызов curl не доказывает, что правило работает для всех вариантов URL. Составьте небольшую тестовую матрицу и меняйте только один параметр запроса за раз: схему, Host, путь, параметры или метод.
HTTP и HTTPS
Проверьте оба протокола отдельно:
curl -I http://example.com/path
curl -I https://example.com/path
Для принудительного перехода на HTTPS ожидайте редирект на первом адресе и обычно ответ 200 на втором, если путь существует. Если HTTPS снова перенаправляет на HTTP или HTTP повторно возвращается на HTTPS, ищите конфликтующие правила и неверное определение исходной схемы.
При проверке конкретного сервера сохраните Host и SNI с помощью --resolve:
curl -I --resolve example.com:443:203.0.113.10 https://example.com/path
Здесь curl подключается к указанному IP, но отправляет имя example.com в Host и TLS SNI. Такой тест помогает отделить ответ нужного origin-сервера от ответа CDN, балансировщика или другого виртуального хоста.
За reverse proxy переменная $scheme показывает схему текущего соединения с Nginx. Если TLS завершился на балансировщике, Nginx может видеть внутренний HTTP и считать, что клиент пришел по HTTP. Правило, которое без проверки перенаправляет все такие запросы на HTTPS, создаст цикл.
Проверьте, какую схему передает прокси в X-Forwarded-Proto. Доверять этому заголовку можно только после настройки прокси, который сам переписывает его и не передает произвольное значение от клиента.
Основной домен, поддомен и server_name
Каждое имя из конфигурации нужно проверить отдельным запросом:
curl -I https://example.com/old
curl -I https://www.example.com/old
curl -I https://api.example.com/old
Правило может работать для example.com, но не срабатывать для www.example.com, если это имя отсутствует в server_name или попадает в другой блок. Аналогичная проблема возникает для поддоменов, IPv4 и IPv6.
Проверяйте фактический IP и виртуальный хост через --resolve:
curl -I --resolve www.example.com:443:203.0.113.10 https://www.example.com/old
Лишний default_server может перехватывать запросы с неизвестным Host. Отдельный listen [::]:80 нужен для IPv6, если сервер принимает такие соединения. Иначе IPv6-запрос может попасть в другой блок и вернуть код, который не совпадает с результатом IPv4-проверки.
Для отдельного тестового стенда можно использовать VDS или облачный сервер. Например, Timeweb Cloud предоставляет серверы и другую инфраструктуру для размещения тестовых конфигураций Nginx.
Путь, завершающий слеш и query-параметры
Сравните варианты пути, которые реально встречаются в запросах:
curl -I 'https://example.com/old'
curl -I 'https://example.com/old/'
curl -I 'https://example.com/old/item'
curl -I 'https://example.com/old?page=2&utm_source=test'
Путь /old и путь /old/ не идентичны для точного location. Вложенный адрес /old/item может попасть в префиксный или регулярный блок и обойти правило, которое рассчитано только на один URL.
Сравните query string в исходном и целевом адресе. Параметры могут сохраниться автоматически, исчезнуть из-за новой подстановки или продублироваться. Проверяйте отсутствие двойного вопросительного знака, двойного слеша, старого пути и неожиданного переноса параметров в другую часть URL.
Для явного сохранения параметров в целевом URI часто используют переменные $is_args и $args:
return 301 https://example.com/new$is_args$args;
Если параметры отсутствуют, $is_args не добавляет знак вопроса. Если параметры есть, результат содержит ?page=2 и другие значения из исходного запроса.
GET, HEAD и другие HTTP-методы
Сначала сопоставьте HEAD и GET:
curl -I https://example.com/old
curl -i https://example.com/old
curl -i уже отправляет GET, поэтому добавлять -X GET обычно не требуется. Если HEAD возвращает 301, а GET возвращает 200, проверьте обработчик приложения, прокси и ограничения для метода.
Для API и форм используйте безопасный тестовый endpoint и отдельно проверьте исходный метод:
curl -i -X POST --data 'name=test' https://example.com/api/old
Коды 307 и 308 должны сохранять метод и тело при переходе. При 301, 302 и 303 клиент может изменить POST на GET. Не отправляйте повторно боевые операции только ради проверки редиректа. Для тестов используйте staging, идемпотентный endpoint или заранее подготовленный запрос без побочных эффектов.
В таблице проверки фиксируйте исходный метод, код ответа, значение Location и конечный код. Наблюдаемый результат важнее предположения о том, какой блок конфигурации должен был сработать.
Как распознать типовую неисправность по выводу curl
Вывод curl дает достаточно признаков для первичной классификации проблемы. Сначала запишите код, Location, количество переходов и конечный URL, затем связывайте симптом с конкретным уровнем конфигурации.
Location указывает не на тот адрес
Проверьте каждую часть значения Location:
- схема должна соответствовать требуемому HTTP или HTTPS-сценарию;
- домен должен быть целевым, без случайного
wwwили его отсутствия; - порт должен быть доступен клиенту и соответствовать публичному адресу;
- путь не должен терять каталог, добавлять лишний слеш или повторять часть URI;
- query string должна сохраняться, удаляться или изменяться согласно задаче.
Например, ответ location: https://example.com/new/old при ожидаемом https://example.com/new часто указывает на неправильную подстановку URI. Адрес с http:// вместо https:// требует проверки схемы и reverse proxy. Двойной слеш обычно появляется при склейке переменной с завершающим слешем.
Для точной фиксации результата используйте:
curl -sS -D - -o /dev/null https://example.com/old
Сравните полученный заголовок с ожидаемым адресом посимвольно. Такой подход быстро обнаруживает неправильный домен, порт, путь и параметры.
Редирект зациклился
Петля видна по повторяющимся URL или по двум правилам, которые возвращают запрос друг другу. Типовые пары выглядят так:
- HTTP перенаправляется на HTTPS, а прокси снова сообщает Nginx, что исходная схема HTTP;
www.example.comперенаправляется наexample.com, а другой server block возвращает клиента наwww.example.com;/pathполучает завершающий слеш, а правило для/path/удаляет его;- правило для старого пути повторно срабатывает после подстановки нового URI.
Запустите проверку с ограничением переходов:
curl -IL --max-redirs 10 https://example.com/path
Выпишите последовательность вида URL -> код -> Location. Найдите первый повтор. Затем проверьте условие, которое должно исключать уже нормализованный URL: схему, Host, слеш или путь.
Вместо редиректа приходит 200, 404 или 500
| Результат | Вероятная причина | Следующий тест |
|---|---|---|
| 200 | Запрос обработал статический файл, приложение или proxy_pass | Проверить server block, location и порядок выбора маршрута |
| 403 | Запрещен доступ к каталогу, файлу или upstream | Проверить права, allow/deny и обработчик ресурса |
| 404 | Путь попал в другой location или целевой ресурс отсутствует | Сравнить точный URI, server_name и access log |
| 500 | Ошибка приложения, rewrite или внутреннего обработчика | Проверить error log и конфигурацию upstream |
| 502 | Nginx не получил корректный ответ от upstream | Проверить доступность backend и error log Nginx |
Код 404 или 500 не доказывает ошибку именно в директиве rewrite. Сначала определите, какой обработчик сформировал ответ. Полезно сопоставить время запроса, Host и URI в access log с сообщением error log.
Редирект работает только для части URL
Сравните рабочий и нерабочий запросы, изменяя один признак:
- регистр пути, например
/Oldи/old; - завершающий слеш;
- вложенный URI;
- наличие query string;
- основной домен,
wwwи поддомен; - IPv4 и IPv6;
- прямой запрос к origin и запрос через CDN или балансировщик;
- HEAD, GET и допустимый метод API.
Точный location = /old обрабатывает только один URI. Префиксный блок location /old охватывает больше адресов, а регулярное выражение может перехватывать запросы в зависимости от порядка блоков. Флаг ^~ меняет участие регулярных location в выборе обработчика. Проверяйте фактический маршрут через логи, а не по названию блока в файле.
Какие ошибки конфигурации Nginx мешают редиректу
После изменения файла сначала проверьте синтаксис, затем фактически загруженную конфигурацию, выполните reload и только после этого повторите curl-запрос. Изменение файла на диске не меняет поведение уже работающего процесса до успешного применения конфигурации.
Ошибки в return и rewrite
Для простого перенаправления обычно достаточно return:
server {
listen 80;
server_name old.example.com;
return 301 https://new.example.com$request_uri;
}
Директива return сразу завершает обработку запроса и возвращает указанный код. Готовые варианты для переноса сайта, каталога и отдельных страниц через return приведены в статье о настройке редиректов в Nginx без rewrite.
rewrite полезен, когда путь нужно преобразовать по шаблону:
server {
listen 80;
server_name example.com;
rewrite ^/old/?$ /new permanent;
}
Флаг permanent возвращает 301. Флаг redirect возвращает 302. Ошибка в регулярном выражении может расширить охват правила или исключить нужный URI. Якорь ^ ограничивает начало пути, а $ его конец. Без якорей правило для /old может затронуть /old-stock и другие адреса.
Проверяйте соответствие кода задаче. 301 и 308 закрепляют постоянный перенос, 302 и 307 подходят для временных сценариев. Для метода-зависимых запросов выбирайте код, который сохраняет метод, и проверяйте его через POST или другой допустимый метод.
При сборке целевого URL не смешивайте переменные с лишними слешами. Для сохранения исходного пути используйте $request_uri, а для явной работы с параметрами применяйте $is_args$args. После каждой правки проверяйте адреса без параметров и с параметрами.
Неверный server block или location
Если правило выглядит корректно, но curl получает 200, 404 или неожиданный Location, запрос может обслуживать другой блок. Выведите полную загруженную конфигурацию:
sudo nginx -T
Проверьте директивы listen, server_name, default_server и наличие отдельного блока для IPv6. Сопоставьте их с адресом, портом и Host, которые использует curl.
Затем проверьте выбор location. Точный блок с =, самый длинный префикс и регулярные выражения могут привести запрос в разные обработчики. Прокси, alias, обработка статического файла или приложение могут сформировать ответ раньше, чем сработает ожидаемое правило.
Для проверки конкретного блока используйте отдельные Host и IP:
curl -I --resolve example.com:443:203.0.113.10 https://example.com/old
curl -i -H 'Host: example.com' http://203.0.113.10/old
Второй запрос полезен для HTTP-origin, но не заменяет проверку TLS с корректным SNI. Для HTTPS используйте --resolve, чтобы имя сохранялось на уровнях HTTP и TLS.
Проблемы с HTTPS за reverse proxy или CDN
При нескольких уровнях проксирования нужно разделить три значения: схема клиента, схема соединения между прокси и Nginx, схема соединения Nginx с upstream. Переменная $scheme отражает текущий входящий запрос, а не обязательно исходный протокол пользователя.
Петля возникает по такой схеме:
- клиент открывает HTTPS-адрес;
- CDN завершает TLS и отправляет HTTP на Nginx;
- Nginx видит
$scheme=httpи возвращает редирект на HTTPS; - CDN снова подключается к Nginx по HTTP.
Проверьте, передает ли доверенный прокси X-Forwarded-Proto: https, и настроено ли условие, которое использует это значение. Сам клиент не должен иметь возможность произвольно задавать доверенный заголовок, поэтому прокси должен его перезаписывать.
Сравните публичный и прямой запрос к origin. Для публичного адреса используйте обычный curl, для origin сохраните Host и SNI через --resolve. Разница в кодах или Location покажет, на каком уровне появляется перенаправление.
Логи Nginx как подтверждение причины
Access log подтверждает, какой Host, URI, код и upstream status увидел сервер. Error log показывает ошибки синтаксиса, rewrite, proxy и подключения к backend.
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
Для глубокой диагностики маршрутизации используйте пошаговое руководство по логированию и проверке location в Nginx. Оно помогает связать ответ curl с фактически выбранным маршрутом и upstream.
Если стандартного access log недостаточно, временно добавьте поля Host, URI, код, отправленный Location и upstream status:
log_format redirect_debug '$remote_addr host=$host request=$request status=$status location=$sent_http_location upstream_status=$upstream_status scheme=$scheme';
После проверки верните рабочий формат логирования, если расширенный формат нужен только для диагностики. Не оставляйте повышенную детализацию без причины на высоконагруженном сервере.
Безопасная последовательность применения изменений:
sudo nginx -t
sudo nginx -s reload
Вместо сигнала nginx -s reload можно использовать sudo systemctl reload nginx, если сервис управляется systemd. Подробнее о проверке конфигурации, graceful reload и откате описано в руководстве по безопасному обновлению конфигурации Nginx.
Итоговый чек-лист проверки редиректа
Минимальный набор команд
После настройки или изменения правила выполните команды в таком порядке:
- Проверьте синтаксис:
sudo nginx -t. - Примените конфигурацию:
sudo nginx -s reloadилиsudo systemctl reload nginx. - Проверьте исходные заголовки:
curl -I https://example.com/old. - Сравните GET с HEAD:
curl -sS -D - -o /dev/null https://example.com/old. - Посмотрите всю цепочку:
curl -IL --max-redirs 10 https://example.com/old. - Получите конечный код и URL:
curl -Ls -o /dev/null -w '%{http_code} %{url_effective} %{num_redirects}' https://example.com/old. - При проблеме включите подробный вывод:
curl -v -L --max-redirs 5 https://example.com/old. - Проверьте нужный IP и виртуальный хост:
curl -I --resolve example.com:443:203.0.113.10 https://example.com/old. - Сопоставьте запрос с access log и error log.
Что считать проверкой, завершенной успешно
- исходный URL получает ожидаемый код 301, 302, 307 или 308;
Locationсодержит правильные схему, домен, порт, путь и параметры;- цепочка не содержит циклов и лишних переходов;
- конечный URL возвращает ожидаемый код, например 200 для доступной страницы;
- проверены HTTP и HTTPS, основной домен,
www, поддомены и нужные порты; - проверены варианты со слешем, вложенными путями и query string;
- для API и форм проверен исходный HTTP-метод;
- конфигурация прошла
nginx -tи загружена через reload; - при расхождении результат подтвержден access log и error log.
Главные признаки исправной настройки просты: код соответствует задаче, Location ведет точно к нужному адресу, цепочка короткая, петля отсутствует, а конечный ответ ожидаем. Повторяйте эту проверку после каждой правки return, rewrite, server_name, location или схемы работы reverse proxy.