·4 мин чтения

HTTP-статусы, которые врут

Спецификация HTTP описывает, что должен означать каждый статус. Реальные API про это знают, но соблюдают выборочно. Ниже — расхождения, на которые я натыкался чаще всего, и что они означают на практике.

200 с ошибкой внутри

Самое частое и самое неприятное:

HTTP/1.1 200 OK
content-type: application/json

{
  "success": false,
  "error": "INSUFFICIENT_FUNDS",
  "message": "Недостаточно средств"
}

Формально запрос обработан, поэтому 200. Практически — операция не выполнена.

Чем это плохо. Мониторинг считает такой ответ успешным. Графики зелёные, алертов нет, а операции не проходят. Проблему находят по жалобам, а не по метрикам.

Что делать при разборе. Никогда не судить об успехе по статусу, если API так устроен. Смотреть тело всегда. И если делаешь проверку доступности такого сервиса — проверять именно поле в теле, а не код ответа.

404 вместо 403

Ответ на запрос к чужому ресурсу:

HTTP/1.1 404 Not Found

Хотя ресурс существует, просто доступа к нему нет.

Это осознанное решение, а не ошибка. 403 подтверждает существование объекта: перебирая идентификаторы, можно узнать, какие из них реальны. 404 не подтверждает ничего.

Что делать при разборе. Получив 404 на объект, который точно существует, первым делом проверять права, а не искать объект. Я потратил на это не один час, прежде чем понял закономерность.

500 на невалидный ввод

POST /v1/users
{"age": "двадцать"}

HTTP/1.1 500 Internal Server Error

Здесь должен быть 400: ошибка на стороне клиента, сервер работает штатно. 500 означает, что валидации нет вовсе — сервис попытался обработать данные и упал где-то в глубине.

Что делать при разборе. 500 на конкретных данных при работающем сервисе — это почти всегда невалидный ввод. Прежде чем писать «сервис лежит», попробуй тот же запрос с заведомо корректными данными.

401 и 403 — разные вещи

Путают постоянно.

401 Unauthorized — «я не знаю, кто ты». Токена нет, он истёк или подпись не сошлась. Лечится получением нового токена.

403 Forbidden — «я знаю, кто ты, и тебе нельзя». Токен валиден, прав не хватает. Новый токен не поможет, нужны права.

Разница определяет, кому эскалировать: 401 — вопрос к аутентификации, 403 — к настройке доступа.

429 без Retry-After

HTTP/1.1 429 Too Many Requests

И ничего больше. Заголовка Retry-After нет, сколько ждать — неизвестно.

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

Что делать. Экспоненциальная задержка с джиттером: 1с, 2с, 4с, 8с плюс случайная добавка. Джиттер важен — без него все клиенты, попавшие под лимит одновременно, дружно возвращаются тоже одновременно.

502, 503, 504 — три разные истории

Статус Что произошло Куда смотреть
502 Bad Gateway Прокси получил мусор от приложения Приложение упало или вернуло битый ответ
503 Service Unavailable Сервис сам сказал «я не готов» Перегрузка, деплой, healthcheck не проходит
504 Gateway Timeout Приложение не ответило вовремя Медленный запрос, зависшая зависимость, блокировка в базе

504 при этом самый информативный: он говорит, что сервис жив, но что-то внутри него ждёт. Обычно — базу или соседний сервис.

Как это меняет разбор

Практический вывод один: статус — это гипотеза, а не факт. Проверяй его телом ответа, логами и повторным запросом.

Порядок, который редко подводит:

  1. Посмотреть тело ответа целиком, а не только код.
  2. Повторить запрос с заведомо корректными данными — отделить проблему данных от проблемы сервиса.
  3. Повторить запрос от другого пользователя — отделить права от логики.
  4. Посмотреть, что видит сам сервис в логах: его версия событий часто отличается от той, что ушла клиенту.

Что проверить дальше

  • Есть ли перед сервисом прокси, который может подменять статусы — тогда клиент видит не то, что вернуло приложение.
  • Совпадают ли коды в логах приложения с тем, что получил клиент. Расхождение означает, что кто-то по дороге переписал ответ.
  • Как ведёт себя API при явно битом запросе. Это быстрый способ понять, насколько его статусам вообще можно верить.

Обновлено 22 августа 2026