Перейти к содержимому

HTTP статусы

HTTP статусы — трёхзначные коды ответа сервера, сообщающие клиенту результат запроса. Классы: 2xx (успех), 4xx (ошибка клиента), 5xx (ошибка сервера). 200 OK (GET, PUT, PATCH), 201 Created (POST, с заголовком Location), 204 No Content (DELETE, успех без тела), 202 Accepted (асинхронная операция). 400 Bad Request (невалидный JSON, отсутствие полей), 401 Unauthorized (нет токена или истёк), 403 Forbidden (нет прав), 404 Not Found, 409 Conflict (дубликат, конфликт версий), 422 Unprocessable Entity (ошибка бизнес-валидации), 429 Too Many Requests (rate limiting). 500 Internal Server Error (непойманное исключение), 503 Service Unavailable. Типичные ошибки: всегда возвращать 200 с ошибкой в теле, 401 вместо 403, 500 для ошибок валидации клиента, раскрытие деталей (стектрейс) в 5xx.

Введение: Язык ответов сервера

Представьте, что вы звоните в службу поддержки. Вы слышите голос: “Ваш звонок принят”, “Соединяю с оператором”, “Оператор занят, пожалуйста, ждите”, “Номер не существует”. Каждый ответ несёт определённую информацию о состоянии вашего запроса.

В мире HTTP сервер тоже говорит с клиентом на понятном языке. Этот язык — HTTP статус-коды. Трёхзначные числа, которые сообщают клиенту: “Всё хорошо”, “Ты ошибся”, “Я ошибся”, “Ресурс не найден” и так далее.

HTTP статус-коды — это стандартизированные коды ответа сервера, которые сообщают клиенту о результате обработки запроса. Они группируются по диапазонам, где первая цифра указывает на класс ответа.

Правильное использование статус-кодов делает API понятным и предсказуемым. Клиент может реагировать на разные статусы по-разному: повторить запрос при 503, показать “Не найдено” при 404, обработать ошибку валидации при 422.

Классы статус-кодов

ДиапазонКлассЗначениеПримеры
1xxInformationalИнформационные (промежуточные ответы)100 Continue, 101 Switching Protocols
2xxSuccessУспех. Запрос обработан успешно200 OK, 201 Created, 204 No Content
3xxRedirectionПеренаправление. Нужен дополнительный запрос301 Moved Permanently, 304 Not Modified
4xxClient ErrorОшибка клиента. Запрос составлен неправильно или неавторизован400 Bad Request, 401 Unauthorized, 404 Not Found
5xxServer ErrorОшибка сервера. Сервер не смог обработать запрос500 Internal Server Error, 503 Service Unavailable

2xx: Успех

200 OK

Самый распространённый статус. Всё хорошо, запрос выполнен, ответ в теле сообщения.

GET /users/123
HTTP/1.1 200 OK
Content-Type: application/json

{
    "id": 123,
    "name": "Иван"
}

Когда использовать: GET, PUT, PATCH, POST (иногда).

201 Created

Ресурс успешно создан. Ответ должен содержать заголовок Location с URI нового ресурса.

POST /users
Content-Type: application/json

{"name": "Иван"}
HTTP/1.1 201 Created
Location: /users/123
Content-Type: application/json

{
    "id": 123,
    "name": "Иван"
}

Когда использовать: POST, PUT (если ресурс создан).

202 Accepted

Запрос принят в обработку, но ещё не завершён. Ответ не содержит конечного результата. Подходит для асинхронных операций.

POST /reports
{"date": "2024-01-01"}
HTTP/1.1 202 Accepted
Location: /tasks/abc123
Content-Type: application/json

{
    "task_id": "abc123",
    "status": "processing",
    "estimated_time": 30
}

Когда использовать: Асинхронная обработка, очереди, длительные операции.

204 No Content

Запрос выполнен успешно, но ответ не имеет тела. Обычно для операций, которые не возвращают данных.

DELETE /users/123
HTTP/1.1 204 No Content
PATCH /users/123
{"status": "active"}
HTTP/1.1 204 No Content

Когда использовать: DELETE, POST (для операций без возврата), PUT/PATCH (если не нужно возвращать обновлённый ресурс).

3xx: Перенаправление

301 Moved Permanently

Ресурс навсегда перемещён на новый URI. Клиент должен использовать новый URI в будущем.

GET /old-users/123
HTTP/1.1 301 Moved Permanently
Location: /users/123

Когда использовать: Реорганизация API, переход на новые версии.

302 Found (Temporary Redirect)

Ресурс временно находится по другому URI. Клиент продолжает использовать старый URI.

GET /users/123
HTTP/1.1 302 Found
Location: /users/123?version=2

Когда использовать: Временное обслуживание, A/B тестирование.

304 Not Modified

Ресурс не изменился с момента последнего запроса. Клиент может использовать кешированную версию. Тело ответа пустое.

GET /users/123
If-None-Match: "abc123"
HTTP/1.1 304 Not Modified
ETag: "abc123"

Когда использовать: В сочетании с ETag или Last-Modified для эффективного кеширования.

4xx: Ошибка клиента

400 Bad Request

Запрос составлен неправильно. Сервер не может его обработать.

POST /users
Content-Type: application/json

{"name": null}  # name не может быть null
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error": "Invalid request",
    "details": "name cannot be null"
}

Когда использовать: Невалидный JSON, неправильный формат даты, отсутствие обязательных полей.

401 Unauthorized

Клиент не аутентифицирован. Нужно предоставить учётные данные.

GET /users/123
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"

{
    "error": "Authentication required"
}

Когда использовать: Отсутствует токен, истёк токен, неправильный API ключ.

403 Forbidden

Клиент аутентифицирован, но у него нет прав на выполнение операции.

GET /admin/users
Authorization: Bearer user_token
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
    "error": "Insufficient permissions",
    "required_role": "admin"
}

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

404 Not Found

Ресурс не найден.

GET /users/99999
HTTP/1.1 404 Not Found
Content-Type: application/json

{
    "error": "User not found",
    "id": 99999
}

Когда использовать: Несуществующий ID, неправильный URI.

405 Method Not Allowed

HTTP метод не поддерживается для этого ресурса.

DELETE /users/123
# если DELETE не разрешён
HTTP/1.1 405 Method Not Allowed
Allow: GET, PUT, PATCH

Когда использовать: POST на ресурсе, где разрешён только GET.

409 Conflict

Конфликт с текущим состоянием ресурса.

POST /users
{"email": "ivan@example.com"}
# email уже существует
HTTP/1.1 409 Conflict
Content-Type: application/json

{
    "error": "Email already exists",
    "email": "ivan@example.com"
}

Когда использовать: Дубликат уникального поля, конфликт версий (optimistic locking).

422 Unprocessable Entity

Синтаксис запроса правильный, но семантически он некорректен.

POST /orders
{"amount": -100}
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
    "error": "Validation failed",
    "details": {
        "amount": "must be greater than 0"
    }
}

Когда использовать: Валидация бизнес-правил, проверка данных.

429 Too Many Requests

Клиент превысил лимит запросов (rate limiting).

GET /users
# 1001-й запрос за минуту
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json

{
    "error": "Rate limit exceeded",
    "limit": 1000,
    "reset": 60
}

Когда использовать: При ограничении частоты запросов.

5xx: Ошибка сервера

500 Internal Server Error

Стандартная ошибка сервера. Что-то пошло не так, но сервер не может уточнить.

GET /users/123
HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
    "error": "Something went wrong",
    "request_id": "req_abc123"
}

Когда использовать: Непойманное исключение, ошибка базы данных, сбой в коде.

Важно: Не возвращать детали ошибки (стектрейс) в production — это безопасность и информативность.

501 Not Implemented

Метод или функциональность не реализованы на сервере.

PATCH /users/123
# если PATCH не реализован
HTTP/1.1 501 Not Implemented

Когда использовать: Функциональность в плане, но ещё не сделана.

503 Service Unavailable

Сервис временно недоступен (перегрузка, технические работы).

GET /users/123
HTTP/1.1 503 Service Unavailable
Retry-After: 3600
Content-Type: application/json

{
    "error": "Service is under maintenance",
    "estimated_ready": "2024-01-15T12:00:00Z"
}

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

Какой статус выбрать

Успешные операции

ОперацияСтатусТелоЗаголовок Location
GET (один ресурс)200Ресурс-
GET (коллекция)200Список ресурсов-
POST (создание)201Созданный ресурсURI нового ресурса
PUT (обновление)200Обновлённый ресурс-
PATCH (обновление)200Обновлённый ресурс-
DELETE (удаление)204(пусто)-
Асинхронная операция202Информация о задачеURI задачи

Ошибки клиента

СитуацияСтатус
Невалидный JSON400
Отсутствует обязательное поле400
Неверный формат даты400
Нет токена авторизации401
Истёк токен401
Нет прав на операцию403
Ресурс не найден404
Метод не поддерживается405
Дубликат уникального поля409
Бизнес-валидация не пройдена422
Превышен лимит запросов429

Ошибки сервера

СитуацияСтатус
Непойманное исключение500
Ошибка подключения к БД500
Функциональность не реализована501
Сервис временно недоступен503

Кастомные статусы

Не придумывайте свои статусы. Используйте стандартные. Стандартных достаточно.

Плохо:

HTTP/1.1 420 Enhance Your Calm
HTTP/1.1 450 Blocked by Parental Controls

Хорошо: Используйте существующие или расширяйте тело ответа дополнительной информацией.

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error": "VALIDATION_FAILED",
    "code": "CUSTOM_001",
    "message": "Email already exists"
}

Статусы в теле ответа

Некоторые API (особенно не RESTful) возвращают статус в теле:

HTTP/1.1 200 OK
{"status": "error", "code": 404, "message": "User not found"}

Почему это плохо:

  • Клиент должен парсить тело, чтобы понять статус
  • Стандартные инструменты (браузер, прокси) не поймут такой ответ
  • HTTP кеширование работает по статус-кодам, не по телу

Лучше: Использовать правильный HTTP статус.

HTTP/1.1 404 Not Found
{"error": "User not found"}

Статусы и повтор запросов

Некоторые ошибки можно исправить повторным запросом.

СтатусМожно ли повторитьПримечание
408 Request TimeoutДаКлиент может повторить тот же запрос
429 Too Many RequestsДа, после паузыНужно подождать Retry-After
500 Internal Server ErrorОсторожноЕсли ошибка временная, можно повторить
502 Bad GatewayОсторожноВременная проблема с прокси
503 Service UnavailableДа, после паузыRetry-After указывает, как долго ждать
504 Gateway TimeoutОсторожноТаймаут на шлюзе

Идемпотентность важна: GET, PUT, DELETE можно безопасно повторять. POST — не всегда.

Практические примеры

Пример 1: Создание пользователя

POST /users
{"name": "Иван", "email": "ivan@example.com"}

Успех:

HTTP/1.1 201 Created
Location: /users/123
{"id": 123, "name": "Иван", "email": "ivan@example.com"}

Email уже существует:

HTTP/1.1 409 Conflict
{"error": "Email already exists", "email": "ivan@example.com"}

Невалидные данные:

HTTP/1.1 422 Unprocessable Entity
{
    "error": "Validation failed",
    "details": {
        "name": "cannot be empty",
        "email": "invalid format"
    }
}

Пример 2: Получение пользователя

GET /users/123

Успех:

HTTP/1.1 200 OK
{"id": 123, "name": "Иван"}

Не найден:

HTTP/1.1 404 Not Found
{"error": "User not found", "id": 123}

Нет токена:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
{"error": "Authentication required"}

Пример 3: Обновление пользователя

PATCH /users/123
{"phone": "+7-999-123-45-67"}

Успех (с возвратом):

HTTP/1.1 200 OK
{"id": 123, "name": "Иван", "phone": "+7-999-123-45-67"}

Успех (без возврата):

HTTP/1.1 204 No Content

Конфликт версий (optimistic locking):

HTTP/1.1 409 Conflict
{
    "error": "Resource was modified by another request",
    "current_version": 3,
    "your_version": 2
}

Частые ошибки

Ошибка 1: Всегда возвращать 200 OK

HTTP/1.1 200 OK
{"error": "User not found"}

Почему плохо: Клиент должен парсить тело, чтобы понять, ошибка это или успех.

Как исправить: 404 Not Found.

Ошибка 2: 401 вместо 403

GET /admin/users
Authorization: Bearer user_token (пользователь не админ)
→ 401 Unauthorized

Почему плохо: 401 означает “ты не представился”. 403 — “ты представился, но тебе нельзя”.

Как исправить: 403 Forbidden.

Ошибка 3: 500 для ошибок валидации

POST /users
{"email": "invalid"}
→ 500 Internal Server Error

Почему плохо: Ошибка клиента, не сервера.

Как исправить: 400 Bad Request или 422 Unprocessable Entity.

Ошибка 4: 404 для ресурса, который никогда не существовал

Для коллекции ресурсов 404 иногда возвращают, если коллекция пуста:

GET /users?status=deleted
→ 404 Not Found

Почему плохо: Пустая коллекция — это не “не найдено”. Это валидный ответ.

Как исправить: 200 OK с пустым массивом.

Ошибка 5: Раскрытие деталей ошибки в 500

HTTP/1.1 500 Internal Server Error
{"error": "SQLSTATE[42S02]: Base table or view not found"}

Почему плохо: Информация о структуре базы данных — угроза безопасности.

Как исправить: Общее сообщение, request_id для внутреннего отслеживания.

Резюме для системного аналитика

  1. HTTP статус-коды — это язык ответов сервера. Клиент по коду понимает, что произошло, не парся тело ответа. 200 — хорошо, 404 — не найдено, 500 — ошибка сервера.

  2. 2xx — успех. 200 OK (всё хорошо), 201 Created (ресурс создан), 204 No Content (успех без тела), 202 Accepted (асинхронная операция).

  3. 4xx — ошибка клиента. Клиент что-то сделал не так. 400 (плохой запрос), 401 (не авторизован), 403 (нет прав), 404 (не найдено), 409 (конфликт), 422 (ошибка валидации), 429 (слишком много запросов).

  4. 5xx — ошибка сервера. Сервер не смог обработать запрос. 500 (внутренняя ошибка), 501 (не реализовано), 503 (сервис недоступен).

  5. Никогда не возвращайте 200 с ошибкой в теле. Используйте правильный статус-код. Это делает API предсказуемым и совместимым со стандартными инструментами.

  6. Не раскрывайте детали ошибок в 5xx. Стектейс, пути к файлам, структура БД — угроза безопасности. Логируйте детали внутри, клиенту — общее сообщение и request_id.

Проверка знаний

Вопрос 1 из 4
Что означают HTTP статус-коды?
Какой класс кодов обычно означает успешную обработку?
Какой код обычно подходит для «ресурс не найден»?
Чем 4xx обычно отличаются от 5xx?

Вопросы, где были ошибки