Редиректы в Nginx настраивают двумя способами: директивой return или директивой rewrite. Оба задаются внутри server block или location, описанных в статье про приоритет location в Nginx, но работают по-разному и одинаково легко ломают сайт при небрежной настройке.
return и rewrite: в чём разница
Директива return сразу останавливает обработку запроса и отдаёт клиенту код ответа и, при необходимости, адрес для редиректа. Это самый быстрый и предсказуемый способ сделать редирект: одна строка, минимум нагрузки на CPU.
Директива rewrite меняет сам URI запроса по регулярному выражению и, в зависимости от флага, либо продолжает обработку с новым URI внутри Nginx, либо отправляет клиенту HTTP-редирект. Rewrite гибче, но именно из-за этой гибкости чаще становится причиной циклов и неожиданного поведения.
Когда использовать return для простого редиректа
Если задача — просто отправить клиента на другой адрес без модификации пути, return всегда предпочтительнее rewrite. Официальная документация nginx.org прямо рекомендует return для редиректов, где не нужна подстановка по регулярному выражению.
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
Здесь $request_uri сохраняет исходный путь и query-параметры, поэтому example.com/page?id=5 остаётся example.com/page?id=5, а не превращается в редирект на главную страницу.
Синтаксис rewrite: флаги last, break, redirect, permanent
Директива rewrite принимает регулярное выражение, замену и необязательный флаг:
- last — останавливает обработку текущего location и заново ищет location для нового URI.
- break — останавливает обработку rewrite, но остаётся в том же location.
- redirect — отправляет клиенту HTTP 302 с новым адресом вместо внутренней обработки.
- permanent — отправляет клиенту HTTP 301 с новым адресом.
Без флага redirect или permanent rewrite не создаёт видимый браузеру редирект: новый URI обрабатывается внутри Nginx, и в адресной строке клиента остаётся старый адрес.
Частая ошибка: бесконечный цикл rewrite
Флаг last внутри location, который сам подходит под тот же шаблон rewrite, порождает бесконечный цикл: Nginx переписывает URI, заново ищет location, попадает в тот же блок, снова переписывает URI. Результат — ошибка rewrite or internal redirection cycle в error_log и 500-я ошибка на сайте.
Чтобы избежать цикла, проверяйте условием if ($uri !~ ...), что URI ещё не переписан, либо используйте break вместо last там, где переход в новый location не нужен. При проксировании на backend через reverse proxy на Nginx лишний rewrite часто вообще не нужен — proxy_pass справляется сам.
Коды 301 и 302: когда какой использовать
| Код | Значение | Когда применять |
|---|---|---|
| 301 Moved Permanently | Постоянный редирект | Смена домена, протокола, финальный переезд URL |
| 302 Found | Временный редирект | Технические работы, A/B-тест, временная посадочная страница |
| 307 Temporary Redirect | Временный редирект с сохранением метода | POST-запросы, которые нельзя превращать в GET |
Поисковые системы передают вес страницы через 301, но кэшируют его агрессивно: смена 301 на новый адрес позже потребует времени на переиндексацию. 302 поисковики не запоминают надолго, поэтому для окончательных переездов используют 301.
Практические примеры редиректов
Три частых сценария редиректов на сайте, которые также стоит свериться со статьёй про SSL и Certbot в Nginx при переезде на HTTPS:
# редирект с www на домен без www
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
# редирект с http на https
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
# редирект старого URL на новый с сохранением параметров
rewrite ^/old-page$ /new-page permanent;
Чек-лист перед выкладкой редиректов
- Используйте return вместо rewrite, если не нужна подстановка по регулярному выражению.
- Указывайте $request_uri, чтобы не терять путь и query-параметры при редиректе.
- Проверяйте на бесконечный цикл: nginx -t не всегда находит эту ошибку, тестируйте curl -I.
- Выбирайте 301 для постоянных переездов и 302 для временных.
Return и rewrite решают одну задачу — перенаправление трафика — но с разной ценой: return быстрее и надёжнее для прямых редиректов, rewrite нужен там, где путь запроса действительно меняется по шаблону. Тестируйте каждый новый редирект командой curl -I перед тем, как выкладывать его в production.