# теория · шаг 3 из 7
Коды: кто их назначает
Коротко
Код ответа - три цифры, и первая из них важнее двух остальных: она говорит, что делать дальше. А ещё код почти всегда назначает кто-то один - либо nginx, либо приложение, - и по логу видно, кто именно.
- Пять классов: 1xx - «продолжай», 2xx - «готово», 3xx - «иди туда», 4xx - «ты спросил неправильно», 5xx - «у меня сломалось».
- У 204 и 304 тела нет по определению, у 3xx обязателен заголовок
Location. - Относительный адрес в
return 301 /pathnginx достраивает до абсолютного из заголовкаHost- за прокси это выстреливает. - 1xx - промежуточные: в
access_logони не попадают. - Код может быть любым числом: nginx отдаст
418без названия, потому что названия не знает.
Если пять классов, пустое тело у 204 и достройка Location знакомы - листай до «Теперь сам».
Сначала ответь сам
В конфиге строка:
return 301 /new/;
Клиент пришёл на http://shop.local/old. Что окажется в заголовке Location?
Не /new/. nginx достроит абсолютный адрес, взяв имя из заголовка Host
запроса:
Location: http://shop.local/new/
Схема тоже подставится - та, по которой пришёл запрос. И вот тут заложена мина,
которую находят уже на проде: если nginx стоит за другим прокси и получает
Host: internal-app, то клиента отправят на http://internal-app/new/ - адрес,
которого в интернете не существует. Лечение будет в разделе про обратный прокси,
а причина - здесь.
На что это похоже
Справочное окно на вокзале. Ответов там ровно пять видов.
«Секунду, продолжаю» - вопрос принят, разговор не окончен. «Вот билет» - готово, держи результат. «Это в соседнем окне» - ответ есть, но в другом месте, и номер окна назовут. «Так спросить нельзя» - вопрос неверный: не тот бланк, нет паспорта, поезда с таким номером нет. И «у нас упала система» - вопрос был законный, сломалось у них.
Разница между четвёртым и пятым видом - это и есть разница между 4xx и 5xx, и она про то, кому идти чинить. А не про то, кто виноват: 404 бывает и из-за кривой ссылки на своём же сайте.
Механизм: пять классов и кто их назначает
Правило
Первая цифра говорит клиенту, что делать; кто назначил код - говорит лог.
Прочерк в $upstream_addr означает «решил nginx», адрес - «спросили приложение».
Разбор: чем отличаются ответы разных классов
Четыре ответа с одного стенда, только заголовки:
HTTP/1.1 204 No Content
(ни Content-Length, ни Content-Type, ни байта тела)
HTTP/1.1 301 Moved Permanently
Content-Type: text/html
Content-Length: 169
Location: http://localhost/f.txt
HTTP/1.1 304 Not Modified
Last-Modified: Thu, 03 Sep 2026 15:36:38 GMT
ETag: "6a999406-12c"
(тела нет, Content-Length тоже нет)
HTTP/1.1 418
Content-Length: 0
Что тут стоит заметить.
У 204 и 304 тела нет по определению. Не «пустое тело», а его отсутствие как
часть правила: клиент, получив такой код, за телом не идёт. Поэтому в 304 нет и
Content-Length - иначе браузер стал бы ждать данные, которых не будет.
У редиректа тело есть. Сто шестьдесят девять байт - это готовая HTML-страница
«301 Moved Permanently» на случай, если клиент не умеет ходить по Location
сам. Браузеры её не показывают, а вот в curl без -L она видна.
Код 418 отдался без названия. В статус-строке пусто после числа: nginx знает названия только для кодов, которые умеет отдавать сам. Это хорошее напоминание, что название в статус-строке ни на что не влияет - клиент читает три цифры.
Отдельно стоит 444 - код, которого в стандарте нет. nginx по нему закрывает
соединение, ничего не отвечая, и клиент видит не ошибку HTTP, а обрыв: curl
завершается с кодом 52 «пустой ответ от сервера».
Разбор: где наивное правило подводит
«4xx - вина клиента» неверно. Битая ссылка в собственном шаблоне даёт 404, и клиент тут ни при чём. Правильная формулировка - «запрос в таком виде выполнить нельзя», а кто его таким сделал, код не говорит.
«3xx - это всегда редирект» тоже неверно. В классе живёт 304 Not Modified,
и он не про «иди туда», а про «то, что у тебя, ещё годится». Разница видна по
наличию Location: у редиректов он обязателен, у 304 его нет.
Постоянный редирект браузер запоминает. 301 и 308 кешируются, и человек,
однажды получивший такой ответ, пойдёт по новому адресу, даже если ты уже вернул
конфиг обратно. Чинится очисткой кеша у каждого посетителя, то есть практически
никак. Поэтому включать 301 стоит после проверки на 302, а разница между
парами такая:
| Код | Постоянный | Метод сохраняется |
|---|---|---|
| 301 | да | нет: POST может стать GET |
| 302 | нет | нет: то же самое |
| 307 | нет | да |
| 308 | да | да |
Пары 307/308 появились как раз потому, что старые клиенты превращали POST
в GET при переходе, и форма отправлялась второй раз впустую.
1xx в логе не видно. Промежуточный ответ - не окончательный, и access_log
пишет одну строку на запрос, с итоговым кодом. Проверено: обмен, в котором nginx
отдал 100 Continue, а потом 200 OK, дал в логе одну строку с кодом 200.
Что ломается без этого
Самая дорогая ошибка класса - поспешный 301. Его ставят «на минутку», проверить
переезд, а потом полгода получают письма от людей, у которых сайт открывается не
там. Второе по частоте - редирект на внутреннее имя из-за Host: работает у всех,
кроме тех, кто ходит через внешний балансировщик.
Зачем это в работе
Посмотреть цепочку переходов целиком, не гадая:
curl -sIL -o /dev/null -w '%{http_code} %{url_effective}\n' https://shop.local/old
С флагом -L curl идёт по Location до конца, и в выводе видно каждый шаг.
Цепочка длиннее двух звеньев - уже повод разобраться: каждый лишний переход это
целый круг по сетиКруг по сетиВремя, за которое сигнал доходит до сервера и возвращается. По городу это единицы миллисекунд, до другого континента - под двести. Каждый шаг протокола (установка соединения, шифрование, сам запрос) стоит минимум одного круга., а на мобильной связи круг стоит сотню миллисекунд.
Теперь сам
Приложение отдаёт 500, а в логе nginx строка 502 с адресом бэкенда в
$upstream_addr. Кто из них прав и что случилось?
Оба правы, и это разные события. 500 - код, который бэкенд успел отдать сам:
он жив, у него внутренняя ошибка. 502 nginx ставит, когда ответа не было
вовсе, - значит после той ошибки процесс упал или соединение оборвалось. Смотреть
надо $upstream_status в той же строке: если там 500, клиент получил ошибку
приложения; если прочерк - приложение не ответило, и разбираться надо с его
падением.
Главное
Пять классов: 1xx - промежуточный ответ (в access_log его нет), 2xx - готово,
3xx - «иди туда» (плюс 304 «твоя копия годится»), 4xx - запрос выполнить нельзя,
5xx - сломалось на сервере. У 204 и 304 тела нет по определению, у редиректа
обязателен Location, и относительный адрес nginx достраивает из Host. 301 и
308 постоянные - браузер их запоминает; 307 и 308 сохраняют методМетодПервое слово в строке запроса: GET - дай, POST - прими, HEAD - ответь как на GET, но без тела, DELETE - удали. Сервер вправе обрабатывать разные методы по-разному, а nginx умеет их различать директивой limit_except.. Кто назначил
код, говорит не код, а $upstream_addr в логе.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий