Браузер показывает ERR_TOO_MANY_REDIRECTS, curl с флагом -I крутит одну и ту же пару 301 ответов по кругу, а в конфиге nginx всего одно правило редиректа, которое выглядит совершенно невинно. Дело в том, что rewrite, return и try_files решают внешне похожие задачи, но работают по разным правилам, и перепутать их достаточно легко.
Return: простой безусловный ответ
Директива return это самый прямолинейный способ сделать редирект, она просто возвращает указанный код и адрес, без какой-либо обработки регулярных выражений.
location /old-page {
return 301 /new-page;
}
Здесь все прямолинейно: любой запрос на /old-page получит 301 редирект на /new-page, без вариантов и условий. Проблема начинается, когда таким образом случайно создают цикл, например если на /new-page тоже висит правило, которое возвращает обратно на /old-page.
Rewrite: переписывание URL по регулярному выражению
Rewrite мощнее и опаснее одновременно, потому что работает через регулярные выражения и имеет собственную внутреннюю логику обработки, зависящую от флагов.
location / {
rewrite ^/blog/(.*)$ /articles/$1 permanent;
}
Ключевая ловушка в том, что без флага last или break nginx продолжает применять дальнейшие rewrite правила к уже переписанному URL, и если правило написано так, что переписанный адрес снова совпадает с тем же паттерном, получается бесконечный цикл переписывания на уровне самого nginx, еще до того, как это вообще станет видно браузеру как HTTP редирект.
Классический источник бесконечного редиректа
server {
listen 80;
server_name example.com;
location / {
return 301 https://example.com$request_uri;
}
}
server {
listen 443 ssl;
server_name example.com;
location / {
return 301 https://example.com$request_uri;
# Ошибка: этот server блок сам слушает 443,
# но правило снова редиректит на https, создавая цикл
}
}
Такое случается, когда конфиг для HTTPS редиректа скопировали из другого проекта не глядя, и забыли убрать правило редиректа из блока, который уже и так обслуживает защищенное соединение. Браузер честно следует каждому 301, получает точно такой же 301 в ответ, и через несколько повторов решает, что это цикл, показывая ERR_TOO_MANY_REDIRECTS.
Try_files: попытка найти файл, а не редирект
Try_files часто путают с rewrite, хотя это принципиально другая директива, она не создает HTTP редирект для браузера вообще, а пытается найти подходящий файл или location на стороне сервера без изменения адреса в адресной строке браузера.
location / {
try_files $uri $uri/ /index.html;
}
Здесь nginx последовательно проверяет: существует ли файл с точным именем запроса, затем существует ли такая директория, и если ничего не найдено, отдает /index.html без единого HTTP редиректа, браузер даже не узнает, что произошел этот перебор вариантов на сервере.
Как быстро найти источник цикла
Первый практичный шаг — curl с флагом, показывающим только заголовки, без следования за редиректом автоматически.
curl -I https://example.com/old-page
Каждый вызов покажет ровно один следующий шаг цепочки редиректов, а не всю цепочку сразу. Повторяя эту команду по адресу из предыдущего ответа Location, легко увидеть момент, когда адрес начинает повторяться, а значит именно там и живет цикл.
Второй способ, если правило rewrite подозревается конкретно, проверить его логику через Nginx Rewrite Tester, вставив регулярное выражение и тестовый URL, чтобы увидеть результат подстановки до того, как менять боевой конфиг.
Итоговый чеклист
Return это простой безусловный редирект без обработки регулярных выражений, используйте его для прямолинейных случаев без вариативности адреса.
Rewrite мощнее, но требует явного флага last или break в большинстве случаев, иначе nginx продолжит применять последующие правила к уже переписанному адресу, что может привести к неожиданному циклу.
Try_files вообще не создает HTTP редирект, это серверная попытка найти файл, адрес в браузере при этом не меняется.
При поиске бесконечного редиректа используйте curl с флагом -I пошагово, а не полагайтесь только на поведение браузера, который просто покажет общую ошибку без деталей цепочки.
Перед копированием готовых блоков SSL редиректа из других проектов всегда перепроверяйте, что редирект стоит именно в HTTP блоке на порту 80, а не случайно оказался еще и в HTTPS блоке на 443.