1. 1
  2. 2
  3. 3

# теория · шаг 1 из 3

Шесть решений над каждым запросом

Коротко

Это главный урок раздела: всё остальное в курсе - подробности к нему.

  • На каждый запрос nginx принимает фиксированную цепочку из шести решений: какой server → какой location → не выйти ли сразу → откуда брать содержимое → чем обвесить ответ → что записать в лог.
  • Порядок НЕ зависит от того, как написан конфиг, и не меняется никогда.
  • Поэтому любую поломку «отдаёт не то» разбирают, проходя список сверху вниз: не тот сайт - решение 1, не тот файл - 2 или 4, внезапный редирект - 3, пропали заголовки - 5.
  • Каждый следующий раздел курса - подробный разбор одного из шести решений.

Если цепочка решений уже в голове - листай до «Теперь сам».

На каком шаге сломалось

Симптом с боевого сервера: на домене admin.shop.local вместо админки открывается главная страница магазина. Конфиги обоих сайтов на месте, оба nginx -t проходят.

Прежде чем читать дальше - на каком этапе обработки это могло произойти и сколько таких этапов вообще бывает?

Ответ на второй вопрос: шесть. Ответ на первый - на самом первом, и в конце урока станет понятно, почему остальные пять можно даже не проверять.

На что это похоже

Посылка в сортировочном центре проходит фиксированный набор станций, и порядок станций не зависит от того, что написано на коробке.

Сначала определяют город - по индексу, и только по нему. Потом внутри города определяют участок. Потом смотрят, не надо ли вернуть отправителю - если надо, дальше посылка не едет вообще. Потом решают, откуда её брать: она уже на складе или её надо получить у поставщика. Потом клеят наклейки. И в самом конце записывают в журнал, что и когда отправили.

Никакая надпись на коробке не заставит наклеить наклейку раньше, чем определён город. Ровно так же ни одна директива в конфиге не сработает раньше своего решения.

Механизм: шесть решений

Список короткий, и его стоит держать в голове целиком. Слева - вопрос, справа - то, из чего nginx берёт на него ответ.

1 какой server 2 какой location 3 не выйти ли 4 откуда контент 5 заголовки 6 запись в лог по listen и Host по правилам приоритета return, rewrite - дальше не идём файл с диска ИЛИ proxy_pass add_header, gzip, expires код и размер уже известны
Цепочка одна и та же для любого запроса и любого конфига. Диагностика идёт по ней сверху вниз.

Правило

Любую поломку «отдаёт не то» разбирай, проходя список сверху вниз, и останавливайся на первом решении, которое дало не тот результат. Проверять пятое, не проверив первое, - потерянное время: если запрос попал не в тот server, всё остальное в конфиге отношения к делу не имеет.

Разбор: живой запрос по шагам

Клиент делает GET /api/cart с заголовком Host: shop.local, соединение пришло на порт 80.

Решение 1 - кто отвечает. nginx смотрит на все server-блоки, которые слушают 80-й порт, и ищет среди них тот, чей server_name совпадает с shop.local. Не нашёл - берёт default_server, а если и его нет - первый блок на этом порту. Приложение тут ни при чём: выбор идёт только по порту и заголовку Host.

Решение 2 - какой location. Внутри выбранного блока сравниваются пути. У /api/cart может быть несколько кандидатов: location /, location /api/ и какая-нибудь регуляркаРазбирается в разделе 4, глава «Как выбирается location»: Пять форм записи location - шаблон вроде ~ \.php$, под который подходит целое семейство адресов. Победит не первый и не самый очевидный, а тот, кого выберут довольно неинтуитивные правила приоритета - им посвящён целый раздел.

Решение 3 - не закончить ли прямо здесь. Если в выбранном блоке есть return 301 ..., дальше не происходит ничего: клиент получает редирект. Ни файлов, ни бэкендов.

Решение 4 - откуда брать содержимое. Развилка на две ветки: либо путь превращается в путь на диске (root, alias, try_files), либо запрос уходит на бэкенд (proxy_pass). Одновременно и то и другое не бывает.

Решение 5 - чем обвесить ответ. Заголовки, сжатие, кеш-директивы.

Решение 6 - что записать в лог. Строка появляется ПОСЛЕ того, как ответ отправлен, - поэтому в ней уже известны и код ответаКод ответаПервая цифра говорит, что делать дальше: 2 - готово, 3 - иди по другому адресу, 4 - запрос выполнить нельзя, 5 - сломалось на сервере. Остальные две уточняют., и размер, и время.

Разбор: почему server_name не фильтрует

Наивное правило звучит так: «в блоке написано server_name shop.local, значит на другие имена он не ответит». Вот конфиг, на котором это неверно.

/etc/nginx/conf.d/shop.local.confhttp
server {
    listen 80;
    server_name shop.local;
    root /var/www/shop;
}

Запрос с чужим именем:

$  команда
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: evil.example' http://127.0.0.1/
↳  выводcurl -H 'Host: evil.example' http://127.0.0.1/
200

Двести. Магазин ответил на имя, которого не знает.

Причина - в решении 1: nginx обязан выбрать какой-то блок из тех, что слушают порт. Он ищет совпадение по имени, не находит, ищет default_server, не находит и берёт первый блок на этом порту. Отказаться отвечать он не может: на порту кто-то должен ответить.

Отсюда и объяснение симптома из начала урока. Если админка объявлена в файле, который не попал в собранный конфиг (лежит в sites-available без ссылки, назван .conf.bak), то запрос к admin.shop.local не находит своего блока и достаётся магазину. Проверять при этом location, root и права на файлы - бесполезно: сломалось на первом решении.

Правильный способ не отвечать на чужие имена - завести блок-заглушку, который станет умолчанием:

/etc/nginx/conf.d/00-default.confhttp
server {
    listen 80 default_server;
    server_name _;
    return 444;
}

Код 444 - особый: nginx закрывает соединение, не отвечая вовсе. Подробности про выбор блока и про default_server - в разделе про то, кто отвечает.

Что ломается без этого

«Открывается чужой сайт». Решение 1: имя не совпало, ответил блок по умолчанию или первый на порту.

«Отдаётся не тот файл» или «404 на существующий файл». Решение 2 или 4: выбран другой location либо путь на диске собрался не так, как ожидалось.

«Внезапный редирект в цикл». Решение 3: сработал return или rewrite, до содержимого дело не дошло.

«Пропал заголовок безопасности». Решение 5, и почти всегда это наследование add_header - отдельная ловушка следующего раздела.

Зачем это в работе

Это единственная схема курса, которую стоит помнить наизусть. Она работает даже с чужим конфигом на пятьсот строк, потому что порядок решений не зависит от того, как конфиг написан, - а значит, разбор всегда начинается в одном и том же месте.

Практически это выглядит так: вместо чтения конфига сверху вниз задаёшь шесть вопросов по очереди и на первом же несовпадении получаешь ответ. Разница по времени - десятки минут на каждом инциденте.

Теперь сам

На сервере два сайта. Запрос к https://shop.local/static/app.css возвращает 301 на https://shop.local/. Файл на диске есть, права верные, location /static/ в конфиге объявлен. На каком решении сломалось и что смотреть?

Ответ. На третьем. Права и наличие файла относятся к решению 4, но до него дело не дошло: раз пришёл 301, сработал return или rewrite. Смотреть надо две вещи: return на уровне самого server (он выполняется до выбора location и бьёт всё) и rewrite внутри выбранного блока. Проверка - nginx -T | grep -n "return\|rewrite" плюс rewrite_log on с уровнем notice в error_log, который покажет, какая именно строка сработала. Смотреть права на файл в этой ситуации бессмысленно: цепочка оборвалась раньше.

Главное

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

Комментарии

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

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

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