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
Говорить о каждой из них в рамках этой статьи мы не будем. Для нас сейчас интересны конкретные 4 фазы:
  • 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.
Директива rewrite - именно тот механизм, который может изменить URI и отправить запрос обрабатываться по второму кругу в поисках нового location. return, как и во многих языках программирования, является "точкой выхода" - прекращает дальнейшую обработку запроса и возвращает ответ пользователю.

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

20260529_QydTh2GI
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:
20260529_4m8kPx5S
return 301 https://domain.com$request_uri;Но его можно реализовать и с помощью if:
20260529_L7Fx63hy
if ($scheme = http) {
return 301 https://domain.com$request_uri;
}

Редирект со старого домена на новый

Синтаксис аналогичен редиректу http -> https. Редактируем блок server старого домена и в URL указываем новый:
20260529_wkABK6AG
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 или ничего.
В результате по запрашиваемому URL мы получаем ответ "404 Not Found" и указанный нами текст, но несмотря на это сам файл photos.html остался на месте:
20260529_VTAoWzmf

Блокировка ботов и не только

Для блокировки нежелательных ботов можно воспользоваться проверкой 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 находится в списке,  получает ошибку о разрыве соединения:
20260529_FN2kMGAB
Это очень полезно для блокировки разного рода мусорного трафика, ботов, парсеров, базовой защиты от атак.

rewrite: больше возможностей

Синтаксис

rewrite regex replacement [flag];
  • regex - регулярное выражение, с которым nginx будет сравнивать URI запроса;
  • replacement - строка, заменяющая исходный URI;
  • flag - необязательный параметр, определяющий дальнейшие действия веб-севрера:
    • last - прекратить обработку текущего блока и отправить nginx искать location для изменённого URI;
    • break - остановить обработку rewrite, но продолжить в текущем location;
    • redirect - вернуть временный редирект (302) на новый URI;
    • permanent - вернуть постоянный редирект (301).

Если флаг не указан, nginx продолжит выполнение следующих rewrite по цепочке в этом же блоке location, что иногда может вызвать перезапись текущих правил и неочевидную работу, поэтому хорошим тоном считается всегда указывать этот параметр.

Контекст

server, location, if.

Применение rewrite

Директива rewrite работает с нормализованным URI, то есть не осуществляет поиск по аргументам. Сами аргументы передаются в новый URI по умолчанию:
location / {
rewrite ^/user$ /user.php last;
}
Или их можно передать с помощью переменной $args:
location / {
rewrite ^/user$ /user.php?$args last;
}
В обоих случаях результат будет одинаковый, однако хорошим тоном будет указать переменную:
20260529_1ZhIEye2
? в конце заменённого URI отключает передачу аргументов:
location / {
rewrite ^/user$ /user.php? last;
}
Это же мы делали когда передавали аргументы через переменную: сперва отключили автоматическую передачу с помощью ?, после чего добавили аргументы с помощью $args.
20260529_Cr5ipio8

Флаги 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:
20260529_lbQTMViB

Редирект с www на домен без www через rewrite

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

Получаем тот же результат:
20260529_SHjOfPrU
Флаг break также заменяет подходящее под указанный regex выражение, но не инициирует поиск нового location для него, а продолжает обработку запроса в текущем блоке.
Это бывает полезным, например для изменения URL отдачи файлов. Для примера настроим доступ к файлам в директории /files по URL /iso:
location /iso/ {
rewrite ^/iso/(.*)$ /files/$1 break;
}
Получаем файл из директории /files по новому URL:
20260529_Aiq4eCa7
Это значит, что 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, а в случае когда запрос не относится к этим файлам, он передаётся на точку входа самого приложения.
20260529_aw3AF5AM

Заключение

Современные тенденции уходят в сторону управления роутингом на стороне самого приложения, с помощью директивы try_files. Но старые утилиты rewrite и return по-прежнему актуальны и помогут вам разобраться в работе вашего сайта. Выбирайте return для организации постоянных простых редиректов и отправки HTTP кодов ответов. rewrite поможет вам со сложной трансформацией URI. Понимая как работают флаги last и break вы вполне сможете перевести ваш .htaccess файл в вид nginx конфигурации и изменить обработчик PHP с устаревшего Apache, на более быстрый PHP-FPM.