# теория · шаг 3 из 6
rewrite: флаги и ловушки
Коротко
rewrite заменяет URI целиком - несовпавший хвост не дописывается сам.
Флаг решает, что дальше: last отправляет искать location заново, break
остаётся здесь и заодно обрывает остальные директивы фазы, включая return
после себя. Замена, начинающаяся со схемы, отвечает клиенту редиректом даже
без флага. Десять перезаписей проходят, одиннадцатая даёт 500.
Флаги знаешь - листай до «Теперь сам». Разборы про то, почему break отдал 404 и куда делся хвост адреса.
Что вернётся на запрос /break/файл?
location /break/ {
rewrite ^/break/(.*)$ /цель/$1 break;
return 200 "остались здесь\n";
}
Кажется, что «остались здесь»: break не уводит из location, а return стоит
следующей строкой. На стенде приходит 404.
На что это похоже
last и break - это «вернуться к началу очереди с новым талоном» против
«остаться у этого окна». Но у второго варианта есть побочный эффект, о котором
не пишут: сказав «остаюсь», ты закрываешь и всё остальное окно тоже. break
не просто отменяет повторный поиск location - он прекращает работу всего
модуля перезаписи в этом блоке, и написанные ниже return, set и rewrite
не выполняются.
Механизм: две дороги после замены
Правило
Замена - это новый URI целиком, а не приставка к старому. Всё, что нужно
сохранить, переносится скобками: $1, $2. Флаг решает, куда идти дальше:
| Флаг | Что происходит |
|---|---|
| нет флага | URI заменён, следующие rewrite выполняются, потом поиск location заново |
last |
немедленно остановиться и искать location заново |
break |
остаться в этом блоке; остальные директивы фазы не выполняются |
redirect |
отдать клиенту 302 |
permanent |
отдать клиенту 301 |
Разбор: куда делся хвост адреса
location /хвост/ { rewrite ^/хвост/ /короткий last; }
location /короткий { return 200 "uri=$uri\n"; }
Запрос /хвост/длинный/путь даёт uri=/короткий. Не /короткий/длинный/путь.
В регулярке нет скобок, значит переносить нечего, а замена подставляется как
есть. Ошибка живучая, потому что на коротком адресе /хвост/ разницы не
видно - она вылезает на первом же вложенном пути. Верная запись сохраняет
хвост явно:
rewrite ^/хвост/(.*)$ /короткий/$1 last;
Там же вторая тонкость - строка параметров. Замер на запросе ?a=1:
| замена | что получил целевой блок |
|---|---|
/цель/x |
args=a=1 - исходные параметры дописаны |
/цель/x? |
args= - знак вопроса в конце их отбрасывает |
Разбор: почему break отдал 404
Возвращаемся к блоку из начала урока. break сработал буквально: перезапись
выполнена (/break/файл стал /цель/файл), поиск location не повторяется,
а return 200 ниже не выполняется вовсе - модуль перезаписи в этом блоке
остановлен. Дальше запрос идёт на фазу контента с новым URI, обработчика в
блоке нет, и nginx честно ищет файл /www/цель/файл. Файла нет - 404.
Отсюда единственное место, где break действительно нужен:
location /api/v1/ {
rewrite ^/api/v1/(.*)$ /api/v2/$1 break;
proxy_pass http://app:8000;
}
Обработчик контента (proxy_pass) стоит в том же блоке - значит уходить
некуда, и break тут по делу. Поставь last - и nginx пойдёт искать location
для /api/v2/..., а proxy_pass из этого блока не сработает.
Что ломается без этого
- Замена со схемы - всегда ответ клиенту.
rewrite ^ http://example.org/x;без флага дал 302: если замена начинается сhttp://,https://или$scheme, nginx перестаёт переписывать внутри. Собрал адрес из$schemeдля внутренней перезаписи - получил редирект. - Одиннадцатая перезапись роняет запрос. Замер цепочкой из блоков:
десять
rewrite ... lastподряд проходят и отдают 200, одиннадцатая даёт 500, а вerror_logпоявляетсяrewrite or internal redirection cycle while processing "/ш12"- с именем URI, на котором счётчик кончился. Внутренние перенаправления отerror_pageиtry_filesтратят тот же лимит. rewriteна уровнеserverидёт до выбора location. Проверено: сrewrite ^/старый(.*)$ /новый$1;в блокеserverзапрос/старый/путьпопадает вlocation /новый, аlocation /старыйне выигрывает никогда.- Лимиты считают уже новый адрес. Перезапись из
location /входвlocation /ограниченсlimit_req: четыре запроса подряд дали200 503 503 503. Ограничение применяется к тому блоку, куда пришли, а не к тому, откуда ушли.
Зачем это в работе
Настоящих поводов брать rewrite вместо return немного, и все они про сбор
адреса из кусков.
location /promo/ {
rewrite ^/promo/(.*)$ /landing/$1.html last;
}
/promo/summer превращается в /landing/summer.html и отдаётся статикой, а в
браузере остаётся исходный красивый адрес. Для переезда домена целиком
регулярка не нужна - там дешевле return 301 ... $request_uri, который не
запускает движок регулярных выражений на каждый запрос.
Теперь сам
Конфиг переехавшего раздела. Что придёт на /старое/док.pdf?
location /старое/ {
rewrite ^/старое/(.*)$ /новое/$1 permanent;
return 200 "перезапись\n";
}
Ответ: 301 с адресом http://<твой Host>/новое/док.pdf. Флаг permanent
отвечает клиенту сразу, поэтому return ниже не выполняется - ровно как в
случае с break. И относительная замена достроится до абсолютного адреса из
заголовка Host, как у return 301 /new.
Главное
Замена - это весь новый URI: хвост переносят скобками, иначе он пропадёт.
break остаётся в блоке и обрывает остальные директивы фазы, last уходит
искать location заново. Замена со схемы - редирект без всякого флага. Предел -
десять перезаписей, одиннадцатая даёт 500 со строкой cycle в логе.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий