Rewrite и Return в Nginx. Разбираем на практических примерах
29 May 2026, 20:51:03
Nginx (engine x) - это современный веб-сервер (и не только), который используют около 35% всех сайтов в сети интернет. Это делает его самым популярным веб-сервером. В этой статье мы поговорим о двух директивах, которые встречаются в конфигурационном файле nginx каждого современного сайта - rewrite и return. Их задача - обработка запроса, перенаправление пользователя, внутреннее изменение пути к файлам, подмена URL. Мы разберём как работают обе директивы, их синтаксис, сценарии и целесообразность использования.Фазы обработки запроса в Nginx
Для понимания того как return и rewrite работают с нашим запросом, полезно узнать о том как вообще nginx обрабатывает этот самый запрос.Процесс разбит на 11 фаз, которые отрабатывают поочерёдно:
- NGX_HTTP_POST_READ_PHASE
- NGX_HTTP_SERVER_REWRITE_PHASE
- NGX_HTTP_FIND_CONFIG_PHASE
- NGX_HTTP_REWRITE_PHASE
- NGX_HTTP_POST_REWRITE_PHASE
- NGX_HTTP_PREACCESS_PHASE
- NGX_HTTP_ACCESS_PHASE
- NGX_HTTP_POST_ACCESS_PHASE
- NGX_HTTP_PRECONTENT_PHASE
- NGX_HTTP_CONTENT_PHASE
- NGX_HTTP_LOG_PHASE
- NGX_HTTP_SERVER_REWRITE_PHASE - фаза, на которой обрабатываются директивы перенаправления указанные только в блоке server (вне всех location);
- NGX_HTTP_FIND_CONFIG_PHASE - фаза, на которой nginx ищет конкретный location, основываясь на URI запроса;
- NGX_HTTP_REWRITE_PHASE - nginx выполняет директивы внутри найденного в предыдущей фазе location.
- NGX_HTTP_РOST_REWRITE_PHASE - фаза, в которой запрос направляется в новый блок location, если его URI был изменён на предыдущей фазе. Фактически новый URI повторно проходит фазу NGX_HTTP_FIND_CONFIG_PHASE.
return: когда нужно быстро и просто
Синтаксис
return code [text];
return code URL;
return URL;- code - код состояния HTTP (200, 301, 302, 403, 444 и др.);
- [text] - текст в кавычках. Будет передан в теле ответа;
- URL - URL для перенаправления (коды 30x). Будет передан в заголовке Location.
Контекст
server, location, внутри блока if.Применение return
return применяется в случаях, когда необходимо сделать простое перенаправление или передать ответ без дальнейшей обработки текущего запроса. Он не поддерживает regex, из-за чего прост в синтаксисе и понимании, а также быстрее rewrite. Значение URL может содержать переменные (например $request_uri).Редирект с www на домен без www

if ($host = www.domain.com) {
return 301 https://domain.com$request_uri;
}- 301 - HTTP код ответа (в данном случае "перемещён навсегда");
- domain.com - ваш домен;
- $request_uri - URI запроса с query-параметрами (например /article.php?id=99).
Редирект с http на https
Данный редирект принято делать директивой return из блока server домена с http:
return 301 https://domain.com$request_uri;Но его можно реализовать и с помощью if:
if ($scheme = http) {
return 301 https://domain.com$request_uri;
}Редирект со старого домена на новый
Синтаксис аналогичен редиректу http -> https. Редактируем блок server старого домена и в URL указываем новый:
server {
server_name old-domain.com;
listen 80;
return 301 https://new-domain.com$request_uri;
}Закрыть страницу/раздел/файл на сайте без фактического удаления
Чтобы это сделать необходимо создать location для конкретного URI и с помощью return передать определённый HTTP код ответа. Например "удалим" страницу photos.html на сайте.В конфигурационный файл nginx домена первым location добавляем:
location = /photos.html {
return 404 "No photos :( \n";
}- /photos.html - URI, в ответ на который веб-сервер будет отправлять указанный ответ;
- 404 - HTTP код ответа (в данном случае "Not Found");
- "No photos :(" - текст, который будет отправлен в теле ответа. Вместо него может быть путь до файла, URL или ничего.

Блокировка ботов и не только
Для блокировки нежелательных ботов можно воспользоваться проверкой User-Agent (UA) пользователя (список ботов является просто примером):if ($http_user_agent ~* (bot|crawler|spider|curl|wget|python|scrapy||libwww)) {
return 444;
}User-Agent из запроса сравнивается со списком имен в скобках, в случае совпадений выполняется указанная директива. С помощью return мы передаём нестандартный HTTP код 444. Когда nginx реализует это правило, он разрывает TCP-соединение, при этом не отправляет совершенно никакого ответа, соответственно не тратит дополнительных ресурсов.Пользователь, чей UA находится в списке, получает ошибку о разрыве соединения:

Это очень полезно для блокировки разного рода мусорного трафика, ботов, парсеров, базовой защиты от атак.
rewrite: больше возможностей
Синтаксис
rewrite regex replacement [flag];- regex - регулярное выражение, с которым nginx будет сравнивать URI запроса;
- replacement - строка, заменяющая исходный URI;
- flag - необязательный параметр, определяющий дальнейшие действия веб-севрера:
- last - прекратить обработку текущего блока и отправить nginx искать location для изменённого URI;
- break - остановить обработку rewrite, но продолжить в текущем location;
- redirect - вернуть временный редирект (302) на новый URI;
- permanent - вернуть постоянный редирект (301).
Контекст
server, location, if.Применение rewrite
Директива rewrite работает с нормализованным URI, то есть не осуществляет поиск по аргументам. Сами аргументы передаются в новый URI по умолчанию:location / {
rewrite ^/user$ /user.php last;
}
Или их можно передать с помощью переменной $args:location / {
rewrite ^/user$ /user.php?$args last;
}В обоих случаях результат будет одинаковый, однако хорошим тоном будет указать переменную: 
? в конце заменённого URI отключает передачу аргументов:
location / {
rewrite ^/user$ /user.php? last;
}Это же мы делали когда передавали аргументы через переменную: сперва отключили автоматическую передачу с помощью ?, после чего добавили аргументы с помощью $args.
Флаги last и break
last можно понимать как конечное правило в текущем контексте. Nginx перестаёт выполнять rewrite из текущего блока (location или server) и выполняет поиск по location уже для заменённого URI. Разберём несколько классических примеров.Человекопонятные URL (ЧПУ) на современных сайтах
location / {
rewrite ^/article/(\d+)- /article.php?id=$1 last;
rewrite ^/user/(\w+)$ /user.php?name=$1 last;
}- ^ - начало строки (синтаксис regex);
- $ - конец строки;
- /article/ или /user/ - текст из регулярного выражения;
- (\d+) - шаблон любого числа;
- (\w+) - шаблон строки (сюда же входят и числа);
- - - дефис.
Во всех наших примерах ранее мы использовали флаг last. Благодаря чему веб-сервер nginx брал заменённый URI (в примере это /article.php или /user.php) и искал для него новый location (обрабатывающий .php). В результате получаются красивые URL:

Редирект с www на домен без www через rewrite
Возможно сделать аналог редиректа, который мы разобрали выше в параграфе про return:if ($host = www.domain.com) {
rewrite ^(.*)$ https://domain.com$1 permanent;
}- ^ - начало строки (синтаксис regex);
- $ - конец строки;
- (.*) - выражение охватывает URI (с аргументами);
- $1 - всё что было захвачено выражением (.*);
- permanent - постоянное перенаправление (301).
Получаем тот же результат:

Флаг break также заменяет подходящее под указанный regex выражение, но не инициирует поиск нового location для него, а продолжает обработку запроса в текущем блоке.
Это бывает полезным, например для изменения URL отдачи файлов. Для примера настроим доступ к файлам в директории /files по URL /iso:
location /iso/ {
rewrite ^/iso/(.*)$ /files/$1 break;
}Получаем файл из директории /files по новому URL:
Это значит, что nginx изменил URI с /iso/Windows10.iso на /files/Windows10.iso, но не пошёл искать location для нового пути, а реализовал текущий блок конфигурации (в данном случае отдал статический файл по изменённому пути).
Try_files как альтернатива для rewrite
В конфигурациях сайтов на современных фреймворках (Laravel, Symfony) и не только директива try_files является более продвинутой альтернативой для проверки существования файлов и директорий, перенаправления запросов. Зачастую строка конфигурации с этой директивой выглядит так:location / {
try_files $uri $uri/ /index.php$is_args$args;
}В данном примере веб-сервер проверяет запрашиваемый URI на существование в виде файла ($uri), директории ($uri/) и если ни то, ни другое не было найдено - передаст аргументы, если они есть, в новый URI /index.php, который, как в случае с флагом last у rewrite, далее пойдёт обрабатываться в другой location (в данном случае php).Говоря простым языком, это удобно, когда можно отдать статические файлы самим nginx, а в случае когда запрос не относится к этим файлам, он передаётся на точку входа самого приложения.
