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 при этом самый информативный: он говорит, что сервис жив, но что-то внутри него ждёт. Обычно — базу или соседний сервис.
Как это меняет разбор
Практический вывод один: статус — это гипотеза, а не факт. Проверяй его телом ответа, логами и повторным запросом.
Порядок, который редко подводит:
- Посмотреть тело ответа целиком, а не только код.
- Повторить запрос с заведомо корректными данными — отделить проблему данных от проблемы сервиса.
- Повторить запрос от другого пользователя — отделить права от логики.
- Посмотреть, что видит сам сервис в логах: его версия событий часто отличается от той, что ушла клиенту.
Что проверить дальше
- Есть ли перед сервисом прокси, который может подменять статусы — тогда клиент видит не то, что вернуло приложение.
- Совпадают ли коды в логах приложения с тем, что получил клиент. Расхождение означает, что кто-то по дороге переписал ответ.
- Как ведёт себя API при явно битом запросе. Это быстрый способ понять, насколько его статусам вообще можно верить.
Обновлено 22 августа 2026