1. 1
  2. 2
  3. 3
  4. 4
  5. 5

# конспект · шаг 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 нет вовсе только внешний

Что проверяется при старте, а что нет

↳  выводnginx -t
"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).

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.