Заголовки это метаданные запроса и ответа. Много багов живёт именно в них: неверный формат, сломанный кеш, небезопасные cookie, CORS. Имена заголовков регистронезависимы: content-type и Content-Type одно и то же.
Формат тела
Content-Typeописывает тело своего сообщения: в запросе то, что шлёт клиент, в ответе то, что отдаёт сервер. Пример:application/json; charset=utf-8.Acceptв запросе: какие форматы клиент готов принять.
Ошибки: сервер не принимает тип тела, ответ 415 Unsupported Media Type. Сервер не может отдать ни один из форматов из Accept, ответ 406 Not Acceptable. В Postman тип тела raw по умолчанию Text, и с ним уходит text/plain: отсюда внезапные 415.
Авторизация и контекст
Authorization: Bearer <токен>илиBasic <base64>.Cookieв запросе иSet-Cookieв ответе.User-Agentкто клиент,Originоткуда пришёл кросс-доменный запрос.X-Request-Id,traceparent: идентификатор запроса для поиска в логах. Прикладывайте его к каждому багу на 5xx.
Кеширование
HTTP/1.1 200 OK
Cache-Control: max-age=300
ETag: "v42"
Cache-Control: max-age=300можно использовать копию 5 минут.no-cacheхранить можно, но перед использованием надо перепроверить у сервера. Название обманчиво.no-storeне хранить вообще. Нужен на ответах с персональными данными.privateтолько в браузере пользователя, не в общих прокси.
Условные запросы. Сервер отдаёт версию ресурса в ETag. Клиент в следующий раз шлёт If-None-Match: "v42", и если ничего не изменилось, получает 304 Not Modified без тела.
При записи то же самое защищает от потерянного обновления: PUT с If-Match: "7" применится, только если версия всё ещё 7, иначе 412 Precondition Failed. Отличный тест для конкурентного редактирования: два окна, одна карточка.
Cookies
Set-Cookie: sid=a1b2c3; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=86400
HttpOnlycookie недоступна из JavaScript, XSS не украдёт её черезdocument.cookie.Secureтолько по HTTPS.SameSiteограничивает отправку в кросс-сайтовых запросах:Strictникогда,Laxтолько при переходе по ссылке,Noneвсегда (и только вместе сSecure). Chrome без атрибута считает cookieLax.Max-AgeилиExpiresсрок жизни. Без них cookie живёт до закрытия браузера.
Проверка для cookie сессии: все три флага безопасности на месте, после выхода cookie удаляется, а старое значение не работает на сервере.
CORS
Браузер не даёт скрипту с https://app.shop.ru читать ответы с https://api.shop.ru, если сервер не разрешил это заголовками:
Access-Control-Allow-Origin: https://app.shop.ru
Access-Control-Allow-Credentials: true
Для «непростых» запросов (PUT, DELETE, JSON-тело, свои заголовки) браузер сначала шлёт preflight OPTIONS. Важное для тестировщика:
- CORS проверяет браузер. Postman и curl его не применяют, поэтому «в Postman работает» ничего не доказывает.
- Со звёздочкой в
Allow-Originзапросы с cookie запрещены. - Сервер, который отражает любой
Originвместе сAllow-Credentials: true, уязвим: чужой сайт прочитает данные пользователя. - Частый баг: на ответах с ошибкой нет CORS-заголовков, и фронтенд вместо текста ошибки видит «CORS error».
Служебные заголовки по кодам
Location у 201 и редиректов, Allow у 405, WWW-Authenticate у 401, Retry-After у 429 и 503.
Частые ошибки
- Путать
Content-TypeиAccept. - Считать
no-cacheзапретом кеширования. - Проверять CORS только из Postman.
- Сравнивать имена заголовков с учётом регистра в автотестах.
- Не замечать утечки версий в
ServerиX-Powered-By.
Что спросят на собесе
- Какие заголовки вы проверяете в ответе?
- Чем
Content-Typeотличается отAccept? - Какие флаги cookie вы знаете и зачем они?
- Что такое ETag и как работает 304?
- Что такое CORS и почему в Postman работает, а в браузере нет?