# rewrite и return в Nginx: как настроить редиректы без ошибок

> Разница между rewrite и return в Nginx, флаги last и break, коды 301 и 302, примеры редиректов www, https и старых URL.

Редиректы в Nginx настраивают двумя способами: директивой return или директивой rewrite. Оба задаются внутри server block или location, описанных в статье про [приоритет location в Nginx](https://www.zevshost.net/ru/kb/nginx-location-prioritet/), но работают по-разному и одинаково легко ломают сайт при небрежной настройке.

## 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](https://www.zevshost.net/ru/kb/nginx-reverse-proxy/) лишний 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.zevshost.net/ru/kb/ssl-nginx-certbot/) при переезде на 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;
```

## Чек-лист перед выкладкой редиректов

1. Используйте return вместо rewrite, если не нужна подстановка по регулярному выражению.
2. Указывайте $request\_uri, чтобы не терять путь и query-параметры при редиректе.
3. Проверяйте на бесконечный цикл: nginx -t не всегда находит эту ошибку, тестируйте curl -I.
4. Выбирайте 301 для постоянных переездов и 302 для временных.

Return и rewrite решают одну задачу — перенаправление трафика — но с разной ценой: return быстрее и надёжнее для прямых редиректов, rewrite нужен там, где путь запроса действительно меняется по шаблону. Тестируйте каждый новый редирект командой curl -I перед тем, как выкладывать его в production.

---

Источник: https://www.zevshost.net/ru/kb/nginx-rewrite-return-redirect/
