HTTP-статус-коды, которые действительно вас оставляют в беде — 301 против 3-02, 401 против 403 и поле 5xx
Не словарь. Концентрированный гид по HTTP-кодам состояний, которые вызывают реальные производственные баги — неправильный перенаправление, которое теперь кэшируется вечно, путаница с 401/403, которая утечку вашей системы аутентификации, 429 без Retry-After, и путаница между 503 и 504, которая отправляет вас в отладку неправильного слоя.
Вы отправили перенаправление и оно было неправильного типа. Теперь каждый браузер, который ранее столкнулся с этим перенаправлением, сохранил его в кэше на долгое время, и единственный способ исправить это — ждать истечения кэша или просить пользователей очистить историю браузера. Это 301.
Это не словарь кодов состояния — таких словарей достаточно. Это руководство, которое вы хотите прочитать после того, как изучили таблицу и всё равно отправили неправильный код. Мы рассматриваем те случаи, которые вызывают реальные ошибки: постоянное кэширование вместо временного, ошибки аутентификации, которые раскрывают информацию, неправильная обработка ответов на ограничение скорости и таймауты гейтвейсов, указывающие на неправильный уровень.
301 против 302 против 307 против 308: Матрица перенаправлений
Ошибку, которую делает каждый раз по крайней мере один раз: вы используете 301 Moved Permanently при тестировании перестройки URL. Оно работает. Вы двигаетесь дальше. Затем вы перестраиваете ещё раз. И теперь часть ваших пользователей — те, кто посещал сайт до второго изменения — направляются на старую цель навсегда, и это кэшируется прямо в их браузере.
301 — постоянное перенаправление и оно кэшируется активно. Браузеры устанавливают срок действия кэшированного перенаправления на практически бесконечный период, если вы не установили Cache-Control заголовок. Нет программного способа удалить кэш с браузера пользователя. Если вы ещё не определили структуру URL, используйте 302.
Проблема сохранения метода — это другое дело. Когда браузер следует перенаправлению 301 или 302, он может (и на практике почти всегда делает) снижать POST до GET при перенаправлении. Это технически нарушение оригинальной спецификации HTTP/1.0, но браузеры нормализовали это так давно, что RFC 7231 признаёт это стандартным поведением. Если вы перенаправляете POST — например, после отправки формы — и ожидаете сохранения метода, вам нужно использовать 307 или 308.
| Код | Постоянное? | Сохраняет метод? | Используйте для |
|---|---|---|---|
| 301 | Да | Нет (POST → GET) | Постоянные перемещения URL, где GET допустим после перенаправления |
| 302 | Нет | Нет (POST → GET) | Временные перенаправления, флаги функций, тесты A/B |
| 307 | Нет | Да | Временные перенаправления, где необходимо сохранить метод |
| 308 | Да | Да | Постоянные перенаправления, где необходимо сохранить метод |
Поддержка 308 сейчас надёжна — Chrome, Firefox, Safari и Edge правильно обрабатывают его. Ограничение: некоторые старые клиенты API и HTTP-библиотеки всё ещё не следуют 308, поэтому если вы перенаправляете трафик между машинами и не контролируете ни клиента, ни его HTTP-библиотеку, обязательно проверяйте это явно.
401 против 403: Аутентификация против авторизации
401 означает «вы не идентифицировались». Спецификация требует, чтобы в нём содержался WWW-Authenticate заголовок, указывающий клиенту, как выполнить аутентификацию. Это правильный код, когда нет действительной сессии, нет токена или токен истёк.
403 означает «я знаю, кто вы, и ответ — нет». Пользователь аутентифицирован, но не имеет необходимых прав. Возврат 403 при истечении токена — это ошибка — это 401. Возврат 401 при запросе с действительными, но недостаточными правами — тоже ошибка, и хуже того, он мешает отладке аутентификации: вы будете искать отсутствующие данные, когда реальная проблема — это назначение ролей.
Аргумент по безопасности в пользу возврата 404 вместо 403 является реальным. Если ваш API возвращает 403 при GET /admin/users, вы просто сообщили любому атакующему, что такой эндпоинт существует, и им нужно более высокие привилегии, чтобы добраться до него. Возврат 404 вместо этого не раскрывает никакой информации о существовании ресурса. Это — вопрос выбора: 403 технически правильный и проще отлаживать; 404 — более безопасный выбор для чувствительных эндпоинтов, где важна скрытность.
429: Ограничение скорости бесполезно без Retry-After
Ответ 429 без Retry-After заголовка — это чёрный ящик. Клиент знает, что он был ограничен по скорости. Он не знает, должен ли он ждать 100 мс или 24 часа. Большинство реализаций клиентов либо немедленно повторяют запрос (что перегружает ваш лимитер), либо полностью отказываются.
Retry-After принимает либо целое число (в секундах, чтобы ждать), либо HTTP-дату (когда можно повторить запрос):
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1749254400
The X-RateLimit-* заголовки не указаны в любом RFC — они стали стандартом благодаря API GitHub, который копировали все. Включайте их вместе с Retry-After. Три заголовка Limit/Remaining/Reset сообщают клиенту, где он находится до того, как достигнет предела, что полезнее, чем просто знать, что он достиг предела.
Одно из важных замечаний: Retry-After действительно допустимо при ответах 503, и оно означает то же самое — «вернитесь через это количество секунд». Если ваш сервис временно недоступен из-за технического обслуживания, отправьте 503 с Retry-After вместо того, чтобы просто отключать соединения.
503 против 504: Где произошло сбой?
Эти два кода выглядят похоже, но указывают на совершенно разные уровни сбоев, и их смешивание направляет вас в неправильную сторону отладки.
503 Сервис недоступен означает, что сервер, на который вы направились, не может обработать запрос в данный момент — он перегружен, находится в режиме технического обслуживания или зависимый сервис не работает. Сам сервер отвечал; он просто отклоняет работу.
504 Gateway Timeout означает, что прокси или гейтвей (ваш балансировщик нагрузки, nginx, API-гейтвей, CDN) попытался достичь сервера вверху и не получил ответ вовремя. Сервер, на который вы направились, жив. Тот, что находится за ним, не отвечает.
На практике: если вы находитесь за nginx, а ваш сервер приложения (Node, Rails, Django и т.д.) аварийно останавливается, вы получаете 502 Bad Gateway — nginx получил ответ, но он был бессмысленным. Если сервер приложения полностью перестаёт отвечать, вы получаете 504. Если вы намеренно возвращаете ошибку на уровне приложения, вы получаете 503. Различие важно при диагностике: 504 означает, что нужно проверить сервис вверху, настройки таймаута и сеть. 503 означает, что нужно проверить саму приложение.
422 против 400: Ошибки валидации имеют свой собственный код
400 Ошибка запроса используется для запросов, которые сервер не может распарсить — неправильный JSON, неправильный синтаксис запроса, отсутствующие обязательные заголовки. Это структурная проблема.
422 Невозможно обработать сущность используется для запросов, которые сервер понял, но не может выполнить из-за ошибки валидации — дата-диапазон, где начало идёт после конца, поле электронной почты с неверным адресом, поле количества с отрицательным значением. Запрос синтаксически был корректен. Семантика была неправильной.
Большинство API объединяют оба в 400 и скрывают детали валидации в теле ответа. Это работает, но делает сложнее обработку ошибок на стороне клиента: клиенты должны анализировать тело ответа, чтобы понять, является ли это ошибкой парсинга или ошибкой валидации. Использование 422 для ошибок валидации позволяет клиентам определять тип ошибки по статусу. Rails делал это правильно с самого начала; спецификация JSON:API также определяет 422 для ошибок валидации.
Одна нюанс: 422 определена в WebDAV (RFC 4918), а не в основной спецификации HTTP. На практике это не имеет значения — все HTTP-клиенты и серверы обрабатывают его без проблем — но вы можете столкнуться с педантом, который утверждает, что вы должны использовать 400 вместо 422. Они не совсем ошибаются. Но 422 более специфичен и широко понятен.
204 против 200 с пустым телом: один из них правильный для DELETE
Когда DELETE успешно завершён, правильный ответ — 204 Без содержимого — а не 200 с пустым телом, и не 200 с {"success": true}.
204 — это явный сигнал «успешно выполнено и отсутствует тело ответа». 200 с пустым телом технически допустим, но он неоднозначен — клиенты не знают, было ли тело туда намерено и потеряно, или отсутствие является намеренным. 204 устраняет эту неоднозначность.
То же самое относится к PUT и PATCH, когда вы не возвращаете обновлённый ресурс. Если ваш API возвращает обновлённый объект после PATCH, используйте 200. Если он не возвращает, используйте 204. Не возвращайте 200 с телом {} или null — это 204 в маске.
Одна нюанс: 204 не должен включать сообщение тела по RFC 9110. Если вы возвращаете 204 и случайно включаете тело (некоторые фреймворки позволяют это), некоторые HTTP-клиенты обрабатывают это без проблем, другие — нет. Удаляйте тело в обработчике ответа, а не только в намерении.
Быстрый справочник: коды, которые мешают и почему
| Код | Значение | Общая ошибка | Исправление |
|---|---|---|---|
| 301 | Постоянное перенаправление | Использование во время тестирования — теперь оно кэшируется навсегда | Используйте 302 до тех пор, пока перенаправление не будет подтверждено постоянным |
| 302 | Временное перенаправление | POST переходит в GET при переходе | Используйте 307, если необходимо сохранить метод |
| 307 | Временное перенаправление (без потери метода) | Смешивается с 302 | Используйте при временных перенаправлениях POST/PUT/PATCH |
| 308 | Постоянное перенаправление (с сохранением метода) | Пропускается в пользу 301 из привычки | Используйте вместо 301, когда метод имеет значение |
| 400 | Ошибка запроса (ошибка парсинга) | Используется для ошибок валидации | Используйте 422 для ошибок валидации, где тело было распарсено |
| 401 | Неавторизован | Возвращается, когда пользователь не имеет разрешений (должно быть 403) | Возвращайте 401 только тогда, когда отсутствуют или истекли данные аутентификации |
| 403 | Forbidden | Возвращается вместо 404 для чувствительных эндпоинтов | Рассмотрите 404, когда скрытие существования эндпоинта имеет значение |
| 422 | Невозможно обработать | Объединяется в 400 | Используйте для ошибок валидации, где тело было распарсено |
| 429 | Ограничение скорости | Отсутствует заголовок Retry-After | Всегда включайте Retry-After и заголовки X-RateLimit-* |
| 503 | Сервис недоступен | Смешивается с 504 | Используйте, когда сервер, на который вы направились, не может обработать запрос |
| 504 | Таймаут гейтвейса | Смешивается с 503 | Исследуйте сервис вверху, а не гейтвейс |
| 204 | Нет содержимого | Возвращается 200 с пустым телом вместо этого | Используйте 204 для успешного DELETE/PUT/PATCH без тела ответа |
Если вам нужно быстро найти любой код состояния при отладке, IO Tools имеет справочник по кодам состояния HTTP который охватывает весь диапазон с описаниями и примечаниями о том, когда использовать каждый из них.
Паттерн, лежащий в основе всех этих ошибок
Большинство этих ошибок возникают из одного места: обращение к кодам состояния как к разрозненным категориям («4xx — ошибка клиента, 5xx — ошибка сервера, всё на этом»), а не как к протоколу с конкретными семантиками. Семантика важна, потому что клиенты — браузеры, HTTP-библиотеки, CDN, инструменты мониторинга — действительно опираются на эти коды. CDN не кэширует 200 и 204 одинаково. HTTP-клиент не будет повторять запросы 400 и 503 с одинаковой логикой. Браузер не кэширует 302 и 301 с одинаковым сроком действия.
Используйте правильный код. Затраты равны нулю. Польза при отладке, когда что-то сломалось — не ноль.
Установите наши расширения
Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска
恵 Табло результатов прибыло!
Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!
Подписаться на новости
все Новые поступления
всеОбновлять: Наш последний инструмент was added on Июн 22, 2026
