HTTP-коды состояния: шпаргалка (1xx-5xx) с примерами
Вы открываете DevTools, и вкладка Network наполовину красная. В продакшене ваш эндпойнт отдаёт 502, локально 200, а коллега в Slack только что спросил: «здесь должен быть 401 или 403?». HTTP-коды состояния выглядят просто (три цифры, пять групп), но неверный выбор раскрывает лишнюю информацию, ломает SEO и превращает дежурства on-call в кошмар.
Это полная шпаргалка по HTTP-кодам для практикующих разработчиков. Вы получите три вещи: (1) таблицу-справочник по всем кодам, которые реально встречаются в боевых системах, (2) матрицы выбора для пар, в которых чаще всего ошибаются (301 vs 302, 401 vs 403, 404 vs 410, 502 vs 504), и (3) раздел про инструменты, как смотреть коды состояния через curl, fetch и Python requests. Все коды ниже опираются на RFC 9110 (действующий стандарт семантики HTTP) и на реестр кодов состояния HTTP IANA.
Краткий справочник: все HTTP-коды одним взглядом
Коды, с которыми вы столкнётесь в продакшене, сгруппированные по классам. Сохраните таблицу в закладки; остальная статья объясняет наиболее каверзные из них.
| Код | Название | Когда вы его увидите |
|---|---|---|
| 100 | Continue | Отправка большого тела POST с заголовком Expect: 100-continue |
| 101 | Switching Protocols | Рукопожатие WebSocket, апгрейд до HTTP/2 |
| 103 | Early Hints | Сервер отправляет заголовки Link до основного ответа |
| 200 | OK | Успех по умолчанию для GET, PUT, PATCH |
| 201 | Created | POST, создающий ресурс (возвращает Location) |
| 202 | Accepted | Асинхронная задача поставлена в очередь, работа ещё не сделана |
| 204 | No Content | Успешный DELETE, PUT без тела ответа |
| 206 | Partial Content | Range-запрос, перемотка видео, докачка |
| 301 | Moved Permanently | Старый URL выведен из обращения, поисковики передают ссылочный вес |
| 302 | Found | Временное перенаправление, исходный URL остаётся каноническим |
| 303 | See Other | Шаблон Post/Redirect/Get после отправки формы |
| 304 | Not Modified | Условный GET с совпадающим ETag или If-Modified-Since |
| 307 | Temporary Redirect | Как 302, но метод и тело сохраняются |
| 308 | Permanent Redirect | Как 301, но метод и тело сохраняются |
| 400 | Bad Request | Битый JSON, отсутствует обязательное поле, не прошла проверка схемы |
| 401 | Unauthorized | Нет учётных данных или истёкший токен |
| 403 | Forbidden | Аутентификация прошла, но доступ запрещён |
| 404 | Not Found | Ресурса не существует (или вы его прячете) |
| 405 | Method Not Allowed | POST к эндпойнту, который принимает только GET (нужен заголовок Allow) |
| 408 | Request Timeout | Клиент слишком долго отправлял запрос |
| 409 | Conflict | Конфликт оптимистической блокировки, дубликат ключа |
| 410 | Gone | Ресурс удалён навсегда и не вернётся |
| 415 | Unsupported Media Type | Неверный Content-Type, например XML в JSON-API |
| 422 | Unprocessable Content | Синтаксис верный, семантика нет (ошибка валидации) |
| 425 | Too Early | Риск повтора early-data в TLS 1.3 |
| 428 | Precondition Required | Сервер требует If-Match, чтобы предотвратить потерю обновлений |
| 429 | Too Many Requests | Сработал rate limit (нужен заголовок Retry-After) |
| 451 | Unavailable for Legal Reasons | DMCA, удаление по GDPR, географическая блокировка |
| 500 | Internal Server Error | Необработанное исключение в вашем коде |
| 501 | Not Implemented | Метод или возможность не поддерживается (редко в REST) |
| 502 | Bad Gateway | Вышестоящий сервер вернул некорректный ответ |
| 503 | Service Unavailable | Режим обслуживания или перегрузка |
| 504 | Gateway Timeout | Вышестоящий сервер не ответил вовремя |
| 507 | Insufficient Storage | В WebDAV закончилось место на диске |
| 508 | Loop Detected | Бесконечное перенаправление или рекурсия в WebDAV |
| 511 | Network Authentication Required | Captive portal в Wi-Fi отеля или аэропорта |
Остальная часть статьи разбирает каждый класс с матрицами выбора, антипаттернами и SEO-последствиями неправильного выбора.
Как устроены HTTP-коды (анатомия трёх цифр)
Почему именно три цифры?
HTTP-коды состояния состоят из трёх десятичных цифр, потому что HTTP/0.9 нужен был сигнал фиксированной ширины: достаточно компактный, чтобы парсер быстро ветвился, и достаточно ёмкий, чтобы оставить запас для новых кодов. Три цифры дают 900 возможных значений (100–999); этого с большим запасом, потому что реестр IANA сегодня использует около 60.
Первая цифра задаёт класс. Вторая и третья дают конкретный код внутри класса. Клиент, который не знает кода 418, должен трактовать его как обобщённый 4xx. RFC 9110 §15 формулирует это явно: клиенты обязаны обрабатывать неизвестные коды как x00 своего класса.
Пять категорий одним взглядом
| Класс | Смысл | Нужно ли тело? | Кэшируется по умолчанию? |
|---|---|---|---|
1xx | Информационный, промежуточный ответ, дальше будет ещё | Нет | Нет |
2xx | Успех, запрос понят и принят | Часто | Зависит от метода |
3xx | Перенаправление, нужны дополнительные действия | Опционально | 301, 308 да; 302, 307 нет |
4xx | Ошибка клиента, ваша вина, исправьте запрос | Да (объясните) | Обычно нет |
5xx | Ошибка сервера, наша вина, повтор может помочь | Да (объясните) | Нет |
Колонка «кэшируется по умолчанию» важна. CDN и браузеры агрессивно и навсегда кэшируют 301 и 308. Выбрав в продакшене не тот код перенаправления, его потом тяжело откатить, потому что у пользователей редирект уже закэширован локально. К этому мы вернёмся в разделе про SEO.
Если хочется глубже разобраться в структуре URL (а коды перенаправления как раз с ней работают), статья URL-кодирование и декодирование проводит через percent-encoding, query-строки и побайтовый pipeline, который определяет, что вообще считается валидным URL.
1xx — информационные (когда вы их реально увидите)
Большинство разработчиков годами не сталкиваются с 1xx напрямую. Это промежуточные ответы: сервер сообщает клиенту «я ещё здесь, продолжай». DevTools браузера обычно их прячет, а большинство HTTP-библиотек схлопывают их в финальный ответ.
Для каждого кода ниже справочник статусов ответов HTTP на MDN — удобный второй источник, если хочется свежим взглядом проверить определение.
100 Continue
Клиент отправляет Expect: 100-continue в заголовках и ждёт, прежде чем передавать большое тело запроса. Сервер отвечает 100 Continue, если готов принять тело, или сразу 4xx, если запрос всё равно будет отклонён. Это экономит трафик при больших загрузках: нет смысла отправлять 200 МБ, если сервер всё равно отклонит запрос из-за отсутствующего заголовка.
curl -v -H "Expect: 100-continue" \
-H "Content-Type: application/octet-stream" \
--data-binary @big-file.bin \
https://api.example.com/upload
Если в подробном выводе нет строки < HTTP/1.1 100 Continue, скорее всего, ваш клиент срезал заголовок или сервер его не поддерживает.
101 Switching Protocols
Рукопожатие, превращающее HTTP-соединение в WebSocket или HTTP/2. Клиент отправляет Upgrade: websocket, сервер отвечает 101 Switching Protocols, и с этого момента соединение говорит на другом протоколе. Этот код видно во вкладке Network у любого мессенджера, дашборда реального времени или инструмента совместной работы.
103 Early Hints
Сравнительно новый код (RFC 8297, 2017), позволяющий серверу отправить заголовки Link для preload-подсказок до того, как готов основной ответ. Браузер начинает подгружать CSS и JS, пока сервер ещё рендерит страницу. По состоянию на 2026 год Cloudflare, Fastly и Vercel поддерживают 103 в продакшене; это современная альтернатива HTTP/2 server push, от которого Chrome отказался.
HTTP/1.1 103 Early Hints
Link: </styles.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
HTTP/1.1 200 OK
Content-Type: text/html
...
Проверка на антипаттерн. Если ваш клиент никогда не видит коды 1xx там, где должен, обычно дело в обратном прокси. Старые версии nginx срезают Expect: 100-continue и 103 Early Hints. Проверьте конфигурацию прокси, прежде чем считать сервер сломанным.
2xx — успех (не только 200)
Возвращать 200 OK на всё подряд это самый распространённый запах кода в REST-API. Семейство 2xx несёт семантическую информацию, которая делает клиентов умнее, а кэши эффективнее.
200 OK
Значение по умолчанию. GET возвращает ресурс, PUT возвращает обновлённый ресурс (или 204), PATCH возвращает результат патча. Если нет повода использовать более специфичный код, берите 200.
201 Created
POST, создающий новый ресурс, должен возвращать 201 плюс заголовок Location, указывающий на созданный ресурс. Так RESTful-клиенты узнают канонический URL только что созданной сущности.
HTTP/1.1 201 Created
Location: /api/users/42
Content-Type: application/json
{"id": 42, "name": "Ada Lovelace"}
202 Accepted
Сервер принял запрос, но обработка ещё не завершена. Используйте для асинхронной работы: клиент должен опрашивать статус, подписаться на webhook или обращаться к эндпойнту статуса. Сопроводите ответ идентификатором задачи в теле.
204 No Content
Успех без тела. Часто используется для DELETE (ресурс удалён, что тут возвращать?) и для операций PUT, когда клиент уже знает новое состояние. Браузеры не меняют текущую страницу, если отправка формы вернула 204. Это удобно для действий «выстрелил и забыл» в одностраничных приложениях.
206 Partial Content
Возвращается на range-запросы: клиент попросил байты 1000-2000 заголовком Range: bytes=1000-2000, и сервер отдал ровно этот фрагмент. Стриминг видео, докачка файлов и HTTP-синхронизация полагаются на 206.
Решение: 200 vs 201 vs 204 для POST
| Сценарий | Код | Тело |
|---|---|---|
| POST создаёт новый ресурс | 201 Created | Новый ресурс (или только ID) + Location |
| POST запускает асинхронную работу, результата ещё нет | 202 Accepted | Job ID, URL для опроса |
POST это действие без ресурса (например, /login) | 200 OK | Результат действия (токен, статус) |
| POST успешен, но ответ пуст | 204 No Content | (нет) |
Если не получается выбрать между 200 и 201, спросите себя: «создал ли сервер ресурс, у которого теперь есть собственный URL?» Если да, 201. Если нет, 200.
3xx — перенаправления (301 vs 302 vs 307 vs 308)
Перенаправления это самый часто неправильно используемый класс. Различия между 301, 302, 307 и 308 сводятся к трём независимым вопросам: переезд постоянный или временный, сохраняется ли метод и кэшируется ли ответ.
301 Moved Permanently
Ресурс переехал и не вернётся. Поисковики передают ссылочный вес на новый URL. Браузеры и CDN кэшируют 301 бессрочно: если вы перенаправили /old на /new через 301, а потом передумали, пользователи с закэшированным редиректом будут ходить на /new вечно (или пока не очистят кэш).
Исторически браузеры могли переписывать метод запроса при 301 (POST → GET); именно поэтому HTTP/1.1 ввёл 308, чтобы это починить.
302 Found
Временное перенаправление. Исходный URL по-прежнему канонический, и поисковики продолжают индексировать оригинал. Используйте для маршрутизации A/B-тестов, страниц обслуживания или потоков «войдите, чтобы продолжить».
Как и в случае с 301, браузеры исторически переписывали POST в GET при 302. Если нужно перенаправить POST и оставить его POST, берите 307.
303 See Other
Всегда переписывает метод в GET. Шаблон Post/Redirect/Get: форма отправляет POST на /submit, сервер возвращает 303 с Location: /thank-you, браузер делает GET /thank-you. Перезагрузка страницы благодарности не отправит форму повторно. Именно для этого и придуман 303.
304 Not Modified
Условный ответ. Клиент отправляет If-None-Match: "abc123" (или If-Modified-Since), сервер проверяет, изменился ли ресурс, и если нет, возвращает 304 без тела. Браузер использует свою закэшированную копию. Так каждый CDN и слой кэширования держит ваш сайт быстрым.
307 Temporary Redirect
Как 302, но метод не должен меняться. POST остаётся POST, тело сохраняется. Используйте, когда нужен временный редирект на не-GET запросе.
308 Permanent Redirect
Как 301, но метод не должен меняться. Современный, более безопасный выбор для постоянных редиректов в API, принимающих POST/PUT.
Матрица выбора: какой код перенаправления?
| Постоянное (кэшировать навсегда) | Временное (не кэшировать) | |
|---|---|---|
| Метод можно сменить на GET | 301 Moved Permanently | 302 Found |
| Метод должен остаться прежним | 308 Permanent Redirect | 307 Temporary Redirect |
Особый случай: если вам нужен именно POST → GET (шаблон Post/Redirect/Get), используйте 303 See Other.
Для HTML-страниц с навигацией браузера 301 и 302 обычно подходят, потому что GET остаётся GET. Для API и форм лучше 308 и 307, чтобы не получить неожиданную замену метода.
4xx — ошибки клиента (как выбрать правильный)
4xx означает, что клиент сделал что-то не так. Чем богаче ваш словарь 4xx, тем удобнее пользоваться API: клиенты могут ветвиться по коду, а не парсить строки ошибок.
400 Bad Request
Общая синтаксическая ошибка. Битый JSON, отсутствующее обязательное поле на структурном уровне, запрос, который сервер даже не может разобрать. Если запрос разобрался, но не прошёл бизнес-валидацию, берите 422.
401 Unauthorized vs 403 Forbidden
Самая запутанная пара в HTTP. Разделение простое, как только его увидеть:
401 Unauthorized: в запросе нет валидной аутентификации. Сервер не знает, кто вы. Повторная отправка учётных данных (или обновление токена) может помочь. Ответ обязан содержать заголовокWWW-Authenticateсогласно RFC 9110 §15.5.2.403 Forbidden: сервер знает, кто вы, и всё равно отказывает. Повторная отправка запроса не поможет. Нужны другие учётные данные или другие права.
| Что вы видите | Что это значит |
|---|---|
401 с WWW-Authenticate: Bearer | Нет токена, истёкший токен или невалидный токен |
403 после успешного входа | Вошли, но этому пользователю недоступен этот ресурс |
401 после успешного входа | Баг, скорее всего, нужен 403 |
Антипаттерн: 403-как-404. Некоторые сайты возвращают 403, когда неаутентифицированный пользователь запрашивает /admin/dashboard. Это раскрывает сам факт существования /admin/dashboard. GitHub решает это, возвращая 404 для приватных репозиториев, в которых вы не состоите: с вашей точки зрения ресурс «не существует». Это сознательный выбор «спрятать информацию», а не баг.
404 Not Found vs 410 Gone
Оба говорят «этого ресурса здесь нет». Разница в постоянстве и SEO.
404 Not Found: может существовать, может нет, может вернуться. Поисковики продолжают проверять.410 Gone: здесь был, намеренно удалён, не вернётся. Поисковики выкидывают из индекса гораздо быстрее.
Если вы удалили страницу товара и хотите убрать её из индекса Google прямо сейчас, правильный выбор 410. Если URL просто временно сломан, подойдёт 404.
405 Method Not Allowed
URL существует, но не принимает этот метод. Ответ обязан содержать заголовок Allow со списком поддерживаемых методов.
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
{"error": "POST is not allowed on this endpoint"}
Забыть заголовок Allow — это нарушение контракта № 1 в самописных REST-API.
408 Request Timeout
Клиент начал отправлять запрос и замолчал. Сервер сдался. В отличие от 504 Gateway Timeout, который про вышестоящий сервер, 408 означает «вы, клиент, тянули слишком долго».
409 Conflict
Запрос конфликтует с текущим состоянием. Чаще всего это оптимистическая блокировка. Клиент отправляет If-Match: "etag-v3", а текущий ETag сервера "etag-v4", поэтому обновление отклоняется с 409.
410 Gone
См. выше: окончательное удаление. Полезно, чтобы выкинуть soft-deleted записи из поисковых индексов.
415 Unsupported Media Type
Клиент отправил тело, которое сервер не понимает. POST с XML на JSON-only API получит 415. Ответ должен подсказать допустимые типы.
422 Unprocessable Content
Запрос разбирается нормально, но не проходит семантическую валидацию. RFC 9110 наконец-то перенёс этот код из WebDAV в основную спецификацию в 2022 году. Используйте 422 для ошибок валидации:
{
"error": "validation_failed",
"details": [
{"field": "email", "message": "must be a valid email"},
{"field": "age", "message": "must be at least 13"}
]
}
Если ваш API не может выбрать между 400 и 422, правило простое: 400 означает «я даже не могу это разобрать», 422 означает «я разобрал, но смысла в этом нет».
425 Too Early
Отправляется, когда сервер не хочет рисковать обработкой запроса, который может оказаться повтором early-data в TLS 1.3. В основном касается CDN и обратных прокси.
428 Precondition Required
Сервер настаивает на том, чтобы вы прислали If-Match или If-Unmodified-Since для предотвращения проблемы потерянных обновлений. Применяется в API совместного редактирования.
429 Too Many Requests
Сработал rate limit. Ответ обязан содержать Retry-After (в секундах или в виде HTTP-даты), чтобы воспитанные клиенты могли выдержать паузу.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
{"error": "rate_limited", "limit": 100, "window": "1m"}
451 Unavailable for Legal Reasons
Цифра отсылает к Брэдбери. Сценарий же вполне реальный: блокировки по DMCA, удаление по «праву на забвение» (GDPR) и страновые геоблокировки, всё это поводы для 451. Согласно RFC 7725 ответ должен содержать заголовок Link, указывающий на правовую инстанцию, потребовавшую блокировку.
418 I’m a Teapot (пасхалка)
Да, он реальный. RFC 2324 (первоапрельская шутка 1998 года), и IETF оставила его в реестре, потому что слишком много продуктов в шутку его реализовали. Не отправляйте 418 в реальном API: большинство обратных прокси и балансировщиков нагрузки обработают его неправильно.
Матрица выбора: какой 4xx?
| Ситуация | Код |
|---|---|
| Тело битое или неразбираемое | 400 |
| Нет аутентификации / истёкший токен | 401 |
| Аутентифицирован, но нет прав | 403 |
| URL не существует (или вы его прячете) | 404 |
| URL существовал, намеренно удалён | 410 |
| Неверный HTTP-метод | 405 (с Allow) |
Неверный Content-Type | 415 |
| Конфликт оптимистической блокировки | 409 |
| Ошибка валидации (разобралось, но не валидно) | 422 |
| Сработал rate limit | 429 (с Retry-After) |
| Заблокировано по юридическим причинам | 451 |
5xx — ошибки сервера (что на самом деле сломалось)
5xx это «наша вина». Дежурным инженерам важнее всего, какой именно 5xx поднял их в три ночи: код подсказывает, на каком слое начинать расследование.
500 Internal Server Error
Универсальный отлов. Почти всегда означает, что необработанное исключение долетело до дефолтного обработчика фреймворка. О причине он не говорит ничего, поэтому здесь структурированное логирование важнее самого кода.
501 Not Implemented
Сервер вообще не поддерживает этот метод. Не путать с 405 (этот метод не разрешён для этого URL): 501 говорит «этот сервер вообще не понимает, что такое PROPFIND». В REST-API встречается редко.
502 Bad Gateway
Обратный прокси или балансировщик получил некорректный ответ от вышестоящего сервиса. Тот ответил, но мусором: не тот протокол, битые заголовки, оборванное соединение посреди ответа. Если CDN отдаёт 502, скорее всего, origin падает или возвращает обрезанные тела.
503 Service Unavailable
Сервер намеренно сейчас не обслуживает запросы. Используйте для технических работ или мягкого ответа на перегрузку. Должен содержать Retry-After.
504 Gateway Timeout
Обратный прокси ждал вышестоящий сервис, и тот не ответил вовремя. Вышестоящий медленный или завис: это отличается от 502, где он ответил мусором.
502 vs 504: диагностика на дежурстве
| Что вы видите | Куда смотреть в первую очередь |
|---|---|
502 Bad Gateway | Вышестоящий отвечает невалидными данными: смотрите логи origin на падения, битые ответы, рассогласование протоколов |
504 Gateway Timeout | Вышестоящий висит: проверьте CPU origin, запросы к БД, вызовы внешних API и proxy_read_timeout у прокси |
Типичная путаница: запрос к БД, который выполняется 60 секунд, всплывёт как 504, если у прокси таймаут 30 секунд, но как 500, если у сервера приложения таймаут 90 секунд и он бросает исключение. Корневая причина одна, код разный, лог-строка разная. Научите дашборды показывать оба случая.
507 Insufficient Storage
Специфичен для WebDAV. На сервере закончилось место. Если вы видите это от не-WebDAV API, кто-то перегружает значение кода.
508 Loop Detected
Бесконечная рекурсия в операциях PROPFIND WebDAV. Очень редко.
511 Network Authentication Required
Код для captive portal: Wi-Fi в отеле или аэропорту отдаёт 511, чтобы сказать вашему браузеру «сначала войдите на портал». Ответ содержит Location со страницей портала.
Матрица траблшутинга: какой слой проверять первым
| Код | Приложение | Прокси | БД | Сеть |
|---|---|---|---|---|
500 | Да | — | Возможно (необработанная ошибка БД) | — |
502 | — | Да (вышестоящий битый) | — | Возможно (TCP reset) |
503 | Да (флаг обслуживания) | Да (rate-limit reject) | — | — |
504 | Да (медленный обработчик) | Да (конфиг таймаута) | Да (медленный запрос) | Да (DNS, потери пакетов) |
Распространённые антипаттерны HTTP-кодов
На эти пять ошибок приходится большая часть плохого кода, который мне доводится ревьюить.
1. Заворачивать ошибки в 200 OK
HTTP/1.1 200 OK
{"success": false, "error": "user_not_found"}
Теперь каждая система мониторинга, CDN и кэш считают, что запрос успешен. Логика повторов ломается. Балансировщики, умеющие смотреть на коды, направляют плохой трафик к «здоровым» бэкендам. Этот шаблон пришёл из JSON-RPC и был унаследован GraphQL; у GraphQL это оправдано, потому что частичные успехи требуют пополевого репортинга ошибок. У REST оправданий нет: используйте 4xx для ошибок клиента, 5xx для ошибок сервера, а структурированные подробности кладите в тело.
2. Путать 401 и 403
Если ваши 401 и 403 непоследовательны, атакующие могут зондировать API и выяснять, какие ресурсы существуют. Выберите политику: либо возвращайте 404 на «вы не можете это видеть» (подход GitHub для приватных репозиториев), либо последовательно 403. Непоследовательность приводит к утечке информации.
3. Прятать 403 за 404
Иногда правильно, часто баг. То, что GitHub возвращает 404 на приватные репозитории, сделано намеренно: само существование репозитория является чувствительной информацией. Но если ваш API возвращает 404 на «учётная запись пользователя заблокирована», легитимные пользователи теперь не могут отличить, где они опечатались в логине, а где их забанили. Опишите свою политику явно и применяйте последовательно.
4. Делать 500 общим уловом
Фреймворки делают это слишком легко, и в этом проблема. Каждое необработанное исключение становится 500, и алерты не отличают «БД упала» от «пользователь прислал битый UUID». Ловите ошибки валидации и поднимайте 400 или 422. Ловите NotFound от ORM и поднимайте 404. Оставьте 500 за по-настоящему неожиданными сбоями, а когда поднимаете его, логируйте request ID, чтобы потом можно было сопоставить.
5. Длинные цепочки редиректов
Каждый прыжок стоит одного round trip. Если /old → /intermediate → /canonical, в худшем случае это два дополнительных DNS-запроса и два дополнительных TCP-рукопожатия. Google специально понижает приоритет краулинга для цепочек длиннее 3 прыжков, а браузеры обрезают цепочки редиректов примерно на 20-м, чтобы избежать петель. Схлопывайте цепочки в источнике: в конфиге CDN или в карте редиректов вашего приложения.
HTTP-коды и SEO
Поисковики относятся к кодам состояния как к авторитетным сигналам: оставлять URL, выкидывать его или передавать вес. Ошибётесь, и позиции поедут.
301 vs 302 (передача ссылочного веса)
301 Moved Permanently передаёт PageRank: Google трактует новый URL как канонического получателя всех сигналов, указывающих на старый URL. 302 Found ссылочный вес не передаёт (или передаёт медленно, зависит от эвристик Google). Если вы переименовали URL навсегда, используйте 301. Если перенаправляете гостя на /login, используйте 302.
404 vs 410 vs Soft 404
Google различает три «отсутствующих» состояния:
404 Not Found: Google периодически перепроверяет и какое-то время держит URL в индексе.410 Gone: Google выкидывает URL быстрее, нередко уже на ближайшем цикле краулинга.- Soft 404: термин Google для страницы, которая возвращает
200 OK, но рендерит сообщение «не найдено». Google детектирует это по контенту и всё равно трактует как404, но запрос на краулинг уже потрачен и реальный контент мог быть размыт.
Если вы чистите устаревший индекс, отдавайте настоящие 410 для безвозвратно удалённых URL.
5xx и crawl budget
Краулер Google снижает темп, когда сайт стабильно отдаёт 5xx. Отчёт Crawl Stats в Search Console это показывает: устойчивый всплеск 5xx может уронить ваш crawl budget на дни, и новые страницы будут индексироваться дольше. Считайте долю 5xx SEO-метрикой, а не только метрикой надёжности.
200 OK, который на самом деле сломан
Возвращать 200 OK со страницей ошибки (антипаттерн soft 404) это худший случай для SEO. Google индексирует сообщение об ошибке, не ранжирует его ни по чему и медленно понимает, что страница битая. Всегда возвращайте правильный код состояния с сервера, даже если ваш SPA рендерит дружелюбный UI ошибки.
Как смотреть HTTP-коды (инструменты)
Невозможно починить то, чего не видно. Каждый практикующий разработчик должен свободно владеть как минимум тремя из этих инструментов.
Панель Network в DevTools браузера
Chrome, Firefox и Safari показывают колонку Status во вкладке Network. Кликните правой кнопкой по заголовку колонки и добавьте Status Text, если её не видно. Полезные приёмы:
- Preserve log сохраняет записи между навигациями, чтобы видеть всю цепочку редиректов.
- Фильтр по статусу: введите
status-code:5xx(Chrome), чтобы видеть только серверные ошибки. - Replay XHR: правый клик по запросу → Replay XHR, чтобы повторить его без перезагрузки страницы.
Для редиректов разверните запрос: увидите каждый прыжок и его код состояния.
curl (универсальный ответ)
curl показывает всё. Три шаблона, которые закрывают 90 % отладки:
# Just the status code
curl -o /dev/null -s -w "%{http_code}\n" https://api.example.com/users/1
# Headers only (HEAD request, follow redirects)
curl -I -L https://example.com
# Full verbose with request and response headers
curl -v https://api.example.com/users/1
Когда вы конструируете тестовые URL со спецсимволами в query-строке, используйте --data-urlencode, чтобы curl сам сделал кодирование, либо вставьте URL в наш URL декодер и кодировщик, чтобы увидеть, какие байты в действительности уйдут в сеть.
# curl encodes the query value for you
curl -G "https://api.example.com/search" \
--data-urlencode "q=hello world & friends"
# Sends: GET /search?q=hello%20world%20%26%20friends
JavaScript fetch
Свойство Response.status содержит целочисленный код. Response.ok равно true для любого 2xx.
const res = await fetch('https://api.example.com/users/1');
console.log(res.status); // 200
console.log(res.statusText); // "OK"
console.log(res.ok); // true
if (!res.ok) {
if (res.status === 401) {
// refresh token and retry
} else if (res.status === 429) {
const retryAfter = Number(res.headers.get('Retry-After')) || 1;
await new Promise(r => setTimeout(r, retryAfter * 1000));
} else if (res.status >= 500) {
throw new Error(`Server error: ${res.status}`);
}
}
В axios та же логика живёт в перехватчиках:
import axios from 'axios';
axios.interceptors.response.use(
response => response,
error => {
const status = error.response?.status;
if (status === 401) {
// redirect to login
}
return Promise.reject(error);
}
);
Python requests
import requests
r = requests.get('https://api.example.com/users/1')
print(r.status_code) # 200
print(r.reason) # 'OK'
# Raises requests.exceptions.HTTPError for 4xx/5xx
r.raise_for_status()
# Manual handling
if r.status_code == 429:
retry_after = int(r.headers.get('Retry-After', '1'))
time.sleep(retry_after)
elif 500 <= r.status_code < 600:
raise RuntimeError(f'Server error: {r.status_code}')
raise_for_status() это питоновская идиома «громко падай на 4xx/5xx». Используйте в скриптах, где нужно получить исключение на ошибках, а не ветвиться по status_code.
Postman и Bruno
Оба клиента позволяют утверждать коды состояния прямо в тестовом скрипте:
// Postman/Bruno test script
pm.test("Status is 201", () => {
pm.response.to.have.status(201);
});
pm.test("Has Location header", () => {
pm.expect(pm.response.headers.get('Location')).to.match(/^\/users\/\d+$/);
});
Запускайте такие проверки против staging в CI, чтобы ловить нарушения контракта до продакшена.
FAQ
В чём разница между 401 и 403?
401 Unauthorized означает, что сервер не знает, кто вы: учётные данные отсутствуют, истекли или невалидны. 403 Forbidden означает, что сервер знает, кто вы, и всё равно отказывает. Если другая попытка с другими учётными данными может помочь, берите 401. Если нет, 403.
Когда использовать 301, а когда 302?
301 подходит, когда переезд постоянный: старый URL не вернётся, и вы хотите, чтобы поисковики передали ссылочный вес на новый. 302 для временных перенаправлений, где исходный URL остаётся каноническим (потоки логина, A/B-тесты, страницы технических работ). Для API лучше 308 и 307: они сохраняют метод запроса.
Что значит ошибка 502 Bad Gateway?
502 означает, что обратный прокси или балансировщик получил некорректный ответ от вышестоящего сервера. Тот ответил, но мусором: не тот протокол, битые заголовки или оборванное соединение. Это не то же самое, что 504 Gateway Timeout, при котором вышестоящий вообще не ответил. Куда смотреть в первую очередь: логи origin на падения и обрезанные ответы.
Что такое «soft 404»?
«Soft 404» это страница, которая возвращает 200 OK, но фактически показывает сообщение «не найдено». Google детектирует такие страницы эвристически и всё равно трактует их как 404. Они впустую тратят crawl budget и могут размывать ваш реальный контент. Всегда отдавайте настоящие коды 404 или 410 с сервера, даже если SPA рендерит дружелюбный UI ошибки.
Когда использовать 422 вместо 400?
400 Bad Request подходит, когда сервер не может даже разобрать запрос: битый JSON, отсутствующие структурные поля, синтаксические ошибки. 422 Unprocessable Content для случая, когда запрос разбирается нормально, но не проходит бизнес-валидацию: невалидный формат email, значение вне диапазона, семантически несогласованные поля. Коротко: 400 про синтаксис, 422 про семантику.
Как реагировать на 429 Too Many Requests?
Прочитайте заголовок Retry-After (число секунд или HTTP-дата) и подождите как минимум столько перед повтором. Если Retry-After нет, используйте экспоненциальный backoff с джиттером, начиная примерно с 1 секунды. Никогда не повторяйте сразу: так вас и банят.
Используются ли в 2026 году коды 1xx?
Да, но большинство из них невидимы для прикладного кода. 100 Continue и 101 Switching Protocols входят в базовые возможности HTTP/1.1. 103 Early Hints всё чаще используют Cloudflare, Fastly и Vercel, чтобы отправлять preload-подсказки до основного ответа: это заметно улучшает Largest Contentful Paint. Большинство HTTP-библиотек схлопывают 1xx в финальный ответ, поэтому обычно вы видите их только в DevTools или при curl -v.
А 418 «I’m a teapot» это правда настоящий код?
Да, как ни удивительно. RFC 2324 был первоапрельской шуткой 1998 года, но достаточно продуктов его реализовали, чтобы IETF сохранила его в реестре в RFC 7168. Не отправляйте 418 в продакшене: многие обратные прокси и балансировщики обрабатывают его неправильно, и вне шутки он не несёт никакой пользы.