Лаборатория кодера

Справочник · Веб и HTTP

Коды ответа HTTP

Что означают коды 1xx–5xx, чем 401 отличается от 403, 301 от 308, 502 от 504, и какой код возвращать из своего API в типичных ситуациях.

Обновлено · 8 мин чтения

Код ответа — первое, что сообщает сервер: получилось или нет, и если нет, то по чьей вине. Первая цифра задаёт класс, остальные уточняют. Клиент, который не знает конкретный код, обязан обработать его как общий код своего класса: неизвестный 299 как 200, неизвестный 499 как 400.

КлассСмысл
1xxинформационные, запрос принят, обработка продолжается
2xxуспех
3xxперенаправление, клиенту нужно сделать ещё один шаг
4xxошибка клиента: запрос неверный или недопустимый
5xxошибка сервера: запрос нормальный, но сервер не справился

1xx: информационные

КодНазваниеКогда встречается
100Continueклиент отправил Expect: 100-continue и ждёт разрешения, прежде чем слать большое тело
101Switching Protocolsпереход на другой протокол, например на WebSocket
103Early Hintsранние подсказки: браузер может начать загружать стили и скрипты, пока страница ещё готовится

2xx: успех

КодНазваниеКогда возвращать
200OKобычный успешный ответ с телом
201Createdресурс создан; в заголовке Location адрес нового ресурса
202Acceptedзапрос принят в очередь, но ещё не выполнен
204No Contentуспех без тела, например после удаления или сохранения настроек
206Partial Contentотдана часть файла по заголовку Range, так работает докачка и перемотка видео

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

КодНазваниеМетод при переходеКешируется
301Moved Permanentlyбраузеры меняют POST на GETда
302Foundбраузеры меняют POST на GETнет, если не указано иное
303See Otherвсегда GETнет
304Not Modified—ответ на условный запрос
307Temporary Redirectсохраняетсянет
308Permanent Redirectсохраняетсяда

Как выбрать:

  • Сайт переехал на новый адрес навсегда, только GET-страницы — 301. Поисковики переносят вес на новый адрес.
  • То же, но через адрес идут и POST-запросы API — 308, чтобы метод и тело не потерялись.
  • После отправки формы перенаправить на страницу результата — 303. Это классический шаблон «POST, перенаправление, GET»: обновление страницы не отправит форму второй раз.
  • Временное перенаправление, например на страницу входа, — 302 или 307, если важно сохранить метод.

304 Not Modified — не перенаправление в обычном смысле. Клиент спросил «изменился ли ресурс с прошлого раза» через заголовки If-None-Match или If-Modified-Since, а сервер ответил «нет, берите из кеша» без тела. Подробнее о заголовках кеша в соседней статье.

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

КодНазваниеКогда возвращать
400Bad Requestзапрос не разобрать: битый JSON, нет обязательного параметра
401Unauthorizedнужна аутентификация, или токен неверный либо истёк
403Forbiddenпользователь известен, но прав на действие нет
404Not Foundресурса нет, или его существование не хотят раскрывать
405Method Not Allowedметод не поддерживается для этого адреса; список разрешённых в заголовке Allow
406Not Acceptableсервер не может отдать ответ в формате из Accept
408Request Timeoutклиент слишком долго отправлял запрос
409Conflictконфликт с текущим состоянием: такой логин занят, версия документа устарела
410Goneресурс удалён навсегда и не вернётся
411Length Requiredнужен заголовок Content-Length
412Precondition Failedне выполнено условие из If-Match: кто-то успел изменить ресурс
413Content Too Largeтело больше допустимого, например загружаемый файл
414URI Too Longслишком длинный адрес, часто из-за огромной строки запроса
415Unsupported Media Typeтело не в том формате: ждали JSON, прислали форму
416Range Not Satisfiableзапрошенный диапазон байтов за пределами файла
418I'm a teapotшутка из первоапрельского RFC 2324, в работе не используется
422Unprocessable Contentсинтаксис верный, но данные не проходят проверку
425Too Earlyсервер не хочет обрабатывать запрос, который может быть повтором
428Precondition Requiredсервер требует условный запрос, чтобы не потерять чужие изменения
429Too Many Requestsпревышен лимит запросов; когда повторить, в заголовке Retry-After
431Request Header Fields Too Largeслишком большие заголовки, часто разросшиеся cookie
451Unavailable For Legal Reasonsдоступ закрыт по требованию закона

401 или 403

Самая частая путаница. Название 401 сложилось исторически и вводит в заблуждение: на деле это «мы не знаем, кто вы». Вместе с ним сервер должен прислать заголовок WWW-Authenticate со способом входа. 403 значит «мы знаем, кто вы, но вам сюда нельзя», и повторная попытка с тем же пользователем не поможет.

400 или 422

400 — запрос невозможно прочитать: невалидный JSON, неверный тип поля. 422 — запрос прочитан, но данные не годятся по смыслу: дата окончания раньше даты начала, email уже зарегистрирован. Многие API используют 400 для обоих случаев, это тоже нормально. Важнее вернуть понятное тело с описанием ошибки:

json
{
  "error": "validation_failed",
  "message": "Проверьте поля формы",
  "fields": {
    "email": "Такой адрес уже зарегистрирован",
    "age": "Должно быть число от 14 до 120"
  }
}

Для таких тел есть стандартный формат RFC 9457, «Problem Details»: поля type, title, status, detail, instance и тип содержимого application/problem+json.

404 вместо 403

Иногда сервер отвечает 404 на чужой ресурс, к которому нет доступа. Так не раскрывается сам факт, что ресурс с таким идентификатором существует. Например, на запрос чужого закрытого репозитория хостинги обычно отвечают именно 404.

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

КодНазваниеТипичная причина
500Internal Server Errorнеобработанное исключение в приложении
501Not Implementedсервер не умеет такой метод вообще
502Bad Gatewayвеб-сервер перед приложением, например nginx, получил от него неверный ответ или не смог подключиться
503Service Unavailableперегрузка или плановые работы; можно указать Retry-After
504Gateway Timeoutприложение за веб-сервером не ответило вовремя
507Insufficient Storageна сервере кончилось место для сохранения

Как разбирать 502 и 504

Оба кода выдаёт не само приложение, а то, что стоит перед ним: nginx, балансировщик, облачный шлюз.

  • 502 чаще всего значит, что приложение не запущено, упало во время запроса или слушает не тот порт или сокет. Смотрите журнал ошибок nginx (/var/log/nginx/error.log), там будет connect() failed или upstream prematurely closed connection.
  • 504 значит, что приложение живо, но отвечает дольше таймаута. Причина обычно в медленном запросе к базе или во внешнем вызове. Увеличение таймаута помогает только как временная мера.

Какой код вернуть: короткая памятка для API

СитуацияКод
получили список или объект200
создали объект201 + Location
приняли задачу в фоновую обработку202
удалили, сохранили без ответа204
битый JSON400
нет токена, токен истёк401
нет прав на действие403
нет такого объекта404
объект уже существует, конфликт версий409
данные не прошли проверку422 или 400
слишком много запросов429 + Retry-After
исключение в коде500
сервис на обслуживании503

Как посмотреть код ответа

bash
curl -I localhost:8080/health                      # только заголовки
curl -s -o /dev/null -w '%{http_code}\n' localhost:8080/api/users
curl -sL -o /dev/null -w '%{http_code} %{url_effective}\n' localhost:8080/old

Последняя команда проходит по всем перенаправлениям и показывает итоговый код и адрес. Больше приёмов в шпаргалке по curl. В браузере код виден во вкладке «Сеть» инструментов разработчика.