# конспект · шаг 5 из 5
Конспект: map вместо if
«If is evil» - и чем его заменяют.
Почему if опасен
if внутри location создаёт неявный вложенный location: сработавшее
условие забирает обслуживание запроса себе, и часть директив перестаёт
наследоваться.
Безопасны внутри if только четыре: return, rewrite, set, break.
if ($request_method = POST) { return 405; } # безопасно
if ($http_user_agent ~* "(bot|spider)") { # безопасно
rewrite ^ /robots-only.html last;
}
location /images/ {
add_header X-Site shop;
if ($request_method = GET) {
add_header X-Cached yes; # ← первый заголовок исчезнет
}
}
Замер по трём случаям одного блока:
| запрос | заголовки в ответе |
|---|---|
| условие истинно | только внутренний |
| условие ложно | только внешний |
блока if нет вовсе |
только внешний |
Что проверяется при старте, а что нет
"try_files" directive is not allowed here
А вот proxy_pass внутри if рядом с внешним proxy_pass конфиг принимает.
Замер: один и тот же location отдал бэкенду /внешний/cart без параметра и
/api/cart?x=1 с параметром. На nginx -t как на защиту от if полагаться
нельзя.
Чем заменять
| Задача | Замена вместо if |
|---|---|
| разное поведение по имени сайта | отдельный server |
| разное поведение по URI | отдельный location |
| ограничение по методу | limit_except |
| «есть ли файл» | try_files |
| по адресу клиента | geo |
| всё остальное | map |
map
map $http_user_agent $is_bot {
hostnames; # для доменных имён: маски .example.com и *.example.com
volatile; # не кешировать результат на запрос
default 0;
"~*(bot|spider)" 1;
}
Порядок поиска: точные ключи → маски имён → регулярки сверху вниз →
default. Точное значение сильнее регулярки, даже если та написана выше -
проверено.
Регистр складывается только на латинице. Abc, abc, ABC попадают в
один точный ключ; Точное и точное - в разные, и ~*^БОЛЬШИЕ$ не ловит
большие. То же верно для location ~*.
маска при hostnames |
ловит |
|---|---|
.wild.org |
и wild.org, и a.wild.org |
*.star.org |
только поддомены, сам star.org - нет |
www.* |
любой хвост после www. |
geo - родственник для адресов: тот же принцип, но ключ - IP.
Ловушки джокера
- Два
ifподряд выполняются ОБА. Замер с двумяsetдаётr=AB: это не «первый выигравший», а последовательное выполнение на фазе rewrite. ifсproxy_passвнутри - один блок, два разных адреса на бэкенде.- Значение карты кешируется на запрос. После
rewriteобычная карта по$uriотдаёт значение, посчитанное ДО перезаписи; лечитсяvolatile. - Переменная в
proxy_passотменяет подстановку хвоста:http://$куда/отправит бэкенду ровно/. Хвост дописывают через$request_uri. - CVE-2026-42533: переполнение буфера в
mapс регулярками и захватами, закрыт в 1.31.3 (и 1.30.4). В 1.31.0 закрывали heap overflow в самомngx_http_rewrite_module(CVE-2026-42945).
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий