$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогСправочник по HTTP-заголовкам: как правильно выбирать Content-Type, Cache-Control и Authorization

Справочник по HTTP-заголовкам: как правильно выбирать Content-Type, Cache-Control и Authorization

$ sudo teach IT
·23 июля 2026 г.·9 мин·63
Справочник по HTTP-заголовкам: как правильно выбирать Content-Type, Cache-Control и Authorization

Сконцентрируемся на заголовках, которые чаще всего ломают интеграции и кэширование: что они означают и какие комбинации реально безопасны.

Содержание
Почему именно эти заголовки чаще всего ломают всёContent-Type: как выбирать корректно и не ломать клиентовЧто такое Content-Type на практикеContent-Type для запросов (request): что бывает неправильноContent-Type для ответов (response): согласованность — ключКакие комбинации безопасныПодводные камни интеграций: «мы же отправляли JSON!»Cache-Control: кэширование без сюрпризовБазовая логика HTTP-кэшированияРазберём часто используемые директивыПрактические безопасные пресеты для APIЧастая ошибка: кэширование 401/403/500Cache-Control и ETag: совместимость, которую нельзя игнорироватьAuthorization: формат схемы и защита от несовместимостейЧто означает AuthorizationСамые частые ошибки форматаБезопасные правила работы с Authorization в интеграцияхНужно ли использовать Vary для Authorization?Комбинации: как сочетать Content-Type, Cache-Control и Authorization безопасноСценарий 1: Авторизованный JSON-ответ без кэшированияСценарий 2: Публичный JSON-эндпоинт, который можно кэшировать на CDNСценарий 3: Ошибка авторизацииСценарий 4: Файлы и вложения — где часто путают типыПолевые заметки: как отлаживать проблемы с заголовками1) Всегда сверяйте «что реально ушло по wire»

Интеграции ломаются не от «сложности HTTP», а от мелочей: один сервис неверно выбирает Content-Type, другой не учитывает особенности кэширования, третий по-разному трактует Authorization. В результате вы получаете редкие, трудно воспроизводимые баги: «иногда работает», «кэш залипает», «у некоторых клиентов 401, у других — 200», «а что именно в заголовках — никто не помнит».

В этом материале — практический справочник по трём категориям заголовков, которые чаще всего становятся причиной проблем: Content-Type, Cache-Control и Authorization. Разберём смысл, безопасные комбинации, типичные ошибки и реальные сценарии (в том числе для интеграций и публичных API).


Почему именно эти заголовки чаще всего ломают всё

HTTP-заголовки — это контракт между клиентом, сервером и промежуточными узлами (прокси, балансеры, CDN). Ошибка в заголовке — это ошибка в трактовке контракта:

  • Content-Type влияет на то, как клиент и промежуточные компоненты интерпретируют тело запроса/ответа. Неправильный тип = неверное декодирование, ошибки сериализации, некорректная обработка на уровне фреймворков.
  • Cache-Control определяет, можно ли хранить ответ и при каких условиях. Ошибка в правилах кэширования = утечки данных или «вечный» кэш неверной версии.
  • Authorization управляет доступом и критичен к формату. Малейшее отклонение от ожидаемой схемы/формата = 401/403 и «плавающие» проблемы в разных клиентах.

Content-Type: как выбирать корректно и не ломать клиентов

Что такое Content-Type на практике

Content-Type в HTTP задаёт медиа-тип содержимого тела. Это базовая информация для парсинга:

  • application/json — тело JSON.
  • application/xml — XML.
  • text/plain; charset=utf-8 — текст.
  • multipart/form-data; boundary=... — смешанные части формы.
  • application/x-www-form-urlencoded — типичное представление формы как ключ=значение&...

На сервере Content-Type влияет на то, как фреймворк решит читать тело. На клиенте — как он решит сериализовать/декодировать.

Важно: Content-Type описывает тело в конкретном сообщении, а не «вообще формат API».

Content-Type для запросов (request): что бывает неправильно

Типовые ошибки:

  1. Неправильный медиа-тип при отправке JSON
    Отправляете JSON-строку, но ставите Content-Type: text/plain. Некоторые клиенты/библиотеки начнут распознавать неверно (или не распознают вообще).
  2. Отсутствие charset там, где это критично для текста
    Для text/* и некоторых других типов полезно явно задавать charset=utf-8. По умолчанию многие системы пытаются угадать, а «угадывание» в разных средах может отличаться.
  3. Путаница между application/json и application/problem+json
    Для ошибок часто используют RFC 7807. Это всё ещё JSON, но семантика другая. Если клиент ожидает один тип, а вы отдаёте другой — он может упасть при проверках.
  4. Неправильная кодировка base64
    Например, отправляете бинарные данные в JSON, но не описываете формат и не согласуете. Тут чаще проблема не в Content-Type, а в том, что вы по факту не описали payload.

Content-Type для ответов (response): согласованность — ключ

Если ваша ручка возвращает JSON — отдавайте:

  • Content-Type: application/json; charset=utf-8 (часто charset не обязателен, но для совместимости полезен)

Если возвращаете PDF:

  • Content-Type: application/pdf

Если возвращаете файл:

  • Content-Type: application/octet-stream
  • дополнительно разумно указать Content-Disposition: attachment; filename="..."

Если отдаёте HTML:

  • Content-Type: text/html; charset=utf-8

Пример: корректный JSON-ответ и коды

code
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: no-store

{"id": 123, "status": "active"}

Для валидационных ошибок (опционально, но корректно и предсказуемо):

code
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json; charset=utf-8

{
  "type": "https://example.com/probs/invalid-input",
  "title": "Invalid input",
  "status": 400,
  "detail": "Field 'email' is invalid"
}

Какие комбинации безопасны

Для типичных API «почти всегда безопасные» правила такие:

  • Если тело — JSON → Content-Type: application/json (или application/problem+json для ошибок по RFC 7807).
  • Если тело — текст → text/plain; charset=utf-8 или более конкретный text/*.
  • Если тело — бинарные данные → один из:
    • application/octet-stream
    • или конкретный тип (например image/png, application/pdf)
  • Если это multipart → multipart/form-data; boundary=... — граница должна соответствовать реальному формату тела.

Подводные камни интеграций: «мы же отправляли JSON!»

В интеграциях часто встречается ситуация: сервер ожидает application/json, а клиент отправляет application/json; charset=ISO-8859-1 или вообще text/json. В большинстве случаев парсинг JSON будет работать, но:

  • Некоторые middleware (или шлюзы безопасности/WAF) могут применять правила по точному совпадению.
  • Логирование/обнаружение контента может делать неправильные выводы.
  • В редких случаях клиентская библиотека может выбрать неверный декодер и получить «битый» JSON.

Рекомендация: держите строгий контракт. Если вы принимаете JSON — принимайте разумный набор вариантов, но нормализуйте на своей стороне. Например, на сервере можно трактовать application/*+json как JSON (это полезно для application/vnd.api+json, application/problem+json и т.п.), но не стоит бесконечно расширять список.


Cache-Control: кэширование без сюрпризов

Базовая логика HTTP-кэширования

Кэширование в HTTP — это компромисс между производительностью и корректностью. Cache-Control задаёт правила, но есть нюансы:

  • У разных посредников могут быть свои ограничения и политики.
  • Клиенты и CDN соблюдают заголовки по-разному, особенно если есть требования безопасности.
  • Есть ещё валидаторы (ETag, Last-Modified), которые часто работают совместно с кэшем.

Тем не менее, Cache-Control — главный инструмент.

Разберём часто используемые директивы

Ниже — практический набор, который стоит знать.

no-store

Жёсткая директива: не сохранять ответ нигде, не вести запись в кэши.

  • Используйте для ответов, содержащих чувствительные данные (персональные токены, приватные данные, «секреты»).
  • Подходит для интеграций, где важна корректность и нельзя допустить сохранение.

Пример:

code
Cache-Control: no-store

no-cache

Технически разрешает хранить, но требует проверку у источника перед использованием.

Часто путают с no-store. Разница концептуальная:

  • no-store — «не сохранять»
  • no-cache — «можно хранить, но каждый раз валидировать»

На практике, если у вас нет корректных валидаторов (ETag/Last-Modified) — no-cache может дать странный эффект: кэш может всё равно пытаться использовать/проверять, а проверка будет невозможна.

Пример:

code
Cache-Control: no-cache

max-age=<seconds>

Сколько секунд ответ считается свежим.

  • Хорошо для публичных и неизменяемых ресурсных данных (справочники, статические конфиги версий, метаданные).
  • Для динамики с разной персонализацией — осторожно.

Пример:

code
Cache-Control: public, max-age=300

public / private

  • public — кэшировать могут и промежуточные узлы (CDN, прокси).
  • private — кэширование разрешено только в контексте клиента (обычно в браузере/клиентском кэше), но не CDN.

Типичная ошибка: поставить public для ответов, зависящих от пользователя. Это открывает риск утечки при неправильном кэшировании.

Пример:

code
Cache-Control: private, max-age=60

must-revalidate

Если кэш стал «просроченным», нужно обязательно перепроверить. Полезно, когда вы позволяете хранить данные, но не хотите долгих «устаревших» окон.

Пример:

code
Cache-Control: private, max-age=60, must-revalidate

s-maxage=<seconds>

Как max-age, но применяется к shared caches (CDN/прокси), а не к персональному кэшу.

Пример:

code
Cache-Control: public, s-maxage=300

Это помогает, когда вы хотите кэшировать на CDN, но более строго контролировать браузер.

immutable

Если ответ с этим заголовком соответствует неизменяемому ресурсу (например, ассет с хэшом в имени файла), клиент может не перепроверять его.

Пример:

code
Cache-Control: public, max-age=31536000, immutable

Практические безопасные пресеты для API

Ниже набор «почти всегда безопасных» стратегий.

Приватные/персональные ответы (почти всегда)

  • Cache-Control: no-store

Если нужно оставить минимальную возможность кэширования на клиенте — иногда используют private, no-store или private, max-age=... для не-очень-секретных данных, но это требует аккуратной модели угроз и тестирования.

Для интеграций с токенами и персональными данными самый безопасный стандарт: no-store.

Публичные справочные данные (можно кэшировать)

  • Cache-Control: public, max-age=<small number>
  • или public, s-maxage=<seconds> если хотите больше контроля на CDN

Но важно: данные должны быть либо неизменяемыми, либо с корректным сроком жизни и механизмом инвалидции.

Отладка/частые изменения: нейтральный вариант

  • Cache-Control: no-cache

Это «компромисс», но опять же зависит от того, есть ли у вас внятная схема валидаторов (обычно ETag).


Частая ошибка: кэширование 401/403/500

Большая часть сервисов допускает кэширование ошибок по ошибке, особенно через промежуточные компоненты. Это опасно и приводит к «залипанию» состояния.

Рекомендация: для ошибок, связанных с аутентификацией/авторизацией или сессиями, используйте:

  • Cache-Control: no-store

Пример:

code
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Cache-Control: no-store

{"error":"unauthorized"}

Cache-Control и ETag: совместимость, которую нельзя игнорировать

Cache-Control управляет свежестью, а ETag позволяет корректно валидировать кэш. Типовой безопасный сценарий:

  1. Ответ содержит ETag
  2. Кэш хранит ответ
  3. Клиент/прокси делает запрос с If-None-Match
  4. Сервер возвращает 304 Not Modified при совпадении

Пример:

code
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: public, max-age=60
ETag: "v3-abc123"

{"version":3,"data":"..."}

Если валидаторы отсутствуют, а вы используете no-cache, поведение может быть менее предсказуемым.


Authorization: формат схемы и защита от несовместимостей

Что означает Authorization

Authorization в заголовках запроса обычно содержит креденшелы в одной из схем:

  • Bearer <token> (самый распространённый вариант в OAuth 2.0 / OpenID Connect)
  • Basic <base64(username:password)>
  • Реже: Digest, HOBA, кастомные схемы.

Критически важно: клиент и сервер должны договориться о схеме. Authorization сам по себе не говорит, какую схему вы выбрали — это часть строки.

Самые частые ошибки формата

  1. Отсутствие пробела после схемы
    Должно быть Bearer <token>, а не Bearer<token>.
  2. Смешение регистров/неожиданная капитализация
    Схемы обычно нечувствительны к регистру, но строгие парсеры иногда капризны.
  3. Неверная строка для Basic
    Basic требует base64(username:password) без переводов строки и корректной кодировки.
  4. Отправка уже «разобранного» токена
    Например, клиент формирует заголовок как Authorization: Bearer и отдельным параметром кладёт токен в query. Это часто ломает сервер и провоцирует утечки (query может логироваться и кэшироваться).

Безопасные правила работы с Authorization в интеграциях

  • Всегда отправляйте токен только в заголовке, а не в URL.
  • Для публичного кэширования (CDN) учитывайте, что авторизованные ответы не должны кэшироваться без явной модели. На уровне API часто проще использовать:
    • Cache-Control: no-store для ответов, зависящих от токена
    • корректную проверку Vary (см. ниже)

Пример запроса с Bearer

code
GET /v1/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOi...
Accept: application/json

Нужно ли использовать Vary для Authorization?

Сам по себе Vary относится к кэшированию: он говорит кэшу, по каким заголовкам различать ответы. Если вы отдаёте разные ответы в зависимости от Authorization, и при этом допускаете кэширование — нужно обеспечить, чтобы кэш корректно различал запросы.

Практическая стратегия для большинства интеграций:

  • Не допускать кэширование (no-store) для авторизованных ответов — самый надёжный путь.
  • Если вы вынуждены кэшировать (например, публичные данные, но доступны через токен) — используйте Vary: Authorization (и/или другие заголовки), но тестируйте на конкретной инфраструктуре (CDN/прокси) — разные узлы ведут себя по-разному.

Пример:

code
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: private, max-age=60
Vary: Authorization

Комбинации: как сочетать Content-Type, Cache-Control и Authorization безопасно

Теперь перейдём к главному — к тому, что реально ломает интеграции: «комбо-заголовки».

Сценарий 1: Авторизованный JSON-ответ без кэширования

Когда ответ зависит от пользователя/токена:

  • Content-Type: application/json; charset=utf-8
  • Cache-Control: no-store
  • Authorization — в запросе, а не в ответе (обычно)

Пример:

code
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: no-store

{"userId": 42, "roles":["admin"]}

Это правило снижает риск «подмешивания» чужого кэша.

Сценарий 2: Публичный JSON-эндпоинт, который можно кэшировать на CDN

  • Content-Type: application/json; charset=utf-8
  • Cache-Control: public, s-maxage=300 (или max-age)
  • Authorization отсутствует (или используется только для расширенных данных — тогда осторожно)

Пример:

code
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: public, s-maxage=300, must-revalidate
ETag: "catalog-9f2d"

Сценарий 3: Ошибка авторизации

Здесь важно не кэшировать, потому что причина может измениться (токен истёк/обновился):

code
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Cache-Control: no-store

{"error":"invalid_token"}

Сценарий 4: Файлы и вложения — где часто путают типы

Если вы отдаёте бинарный файл, Content-Type должен быть корректным, иначе клиент может открыть файл как текст или испортить скачивание.

Пример:

code
HTTP/1.1 200 OK
Content-Type: application/pdf
Cache-Control: private, max-age=600

...binary...

Полевые заметки: как отлаживать проблемы с заголовками

1) Всегда сверяйте «что реально ушло по wire»

Многие баги не в логике, а в расхождении между тем, что вы думаете, и тем, что реально отправлено:

Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Простыми словами о защищенном соединении

Простыми словами о защищенном соединении

Базовый курс для обычных людей: что такое защищённое соединение, как оно устроено, что оно умеет и чего не умеет, как выбрать сервис и настроить его на своих устройствах.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Сервис, который не падает - как построить безопасную зашифрованную сеть

Сервис, который не падает - как построить безопасную зашифрованную сеть

Практический курс по безопасности сети: как собрать собственный сервис защищённого доступа - от пустого сервера до подписки с автовыбором - и, что важнее, как потом не уронить его своими руками.

Перейти к курсу

Другие статьи

Проектируем пагинацию правильно: offset vs cursor и стабильные страницы
проектируем

Проектируем пагинацию правильно: offset vs cursor и стабильные страницы

Разберём, почему offset-пагинация ломается на изменениях данных, а cursor-based даёт стабильность. Рассмотрим форматы курсоров, сортировки, идемпотентность и требования к индексу.

25 июля 2026 г.
680
HTTP/HTTPS на пальцах для разработчика: методы, заголовки, коды и типичные ошибки клиентов
http

HTTP/HTTPS на пальцах для разработчика: методы, заголовки, коды и типичные ошибки клиентов

Соберём ментальную модель HTTP и разберём, как правильно строить запросы, что читать в ответах и где чаще всего ломается интеграция.

20 июля 2026 г.
630
Создание Telegram бота в 2026 легко и просто! Полный курсы!
создание

Создание Telegram бота в 2026 легко и просто! Полный курсы!

19 июня 2026 г.
1431
Как выбрать, куда идти в IT: веб, боты, данные — без хаоса
выбрать

Как выбрать, куда идти в IT: веб, боты, данные — без хаоса

Разберём самые понятные направления для новичка (веб, боты, аналитика/данные) и чем они отличаются по шагам обучения. В конце вы получите простой план, как проверить, что вам подходит, и какой стартовый путь выбрать.

5 октября 2026 г.
580
Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть
путь

Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть

Разберём структуру репозитория, как упаковать проект в понятную историю (проблема → решение → запуск → метрики качества) и какие артефакты повышают доверие: скринкасты, архитектурная схема, чек-лист продакшн-готовности.

17 сентября 2026 г.
1070
Как понять, какой язык программирования учить первым: простой тест и выбор
понять

Как понять, какой язык программирования учить первым: простой тест и выбор

Помогу новичку выбрать первый язык без стресса: объясню варианты по целям (веб, боты, анализ данных), что учить в первую очередь и как не потратить месяцы впустую. В конце — краткий план старта на ближайшие недели.

2 октября 2026 г.
630

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

Почему именно эти заголовки чаще всего ломают всёContent-Type: как выбирать корректно и не ломать клиентовЧто такое Content-Type на практикеContent-Type для запросов (request): что бывает неправильноContent-Type для ответов (response): согласованность — ключКакие комбинации безопасныПодводные камни интеграций: «мы же отправляли JSON!»Cache-Control: кэширование без сюрпризовБазовая логика HTTP-кэшированияРазберём часто используемые директивыПрактические безопасные пресеты для APIЧастая ошибка: кэширование 401/403/500Cache-Control и ETag: совместимость, которую нельзя игнорироватьAuthorization: формат схемы и защита от несовместимостейЧто означает AuthorizationСамые частые ошибки форматаБезопасные правила работы с Authorization в интеграцияхНужно ли использовать Vary для Authorization?Комбинации: как сочетать Content-Type, Cache-Control и Authorization безопасноСценарий 1: Авторизованный JSON-ответ без кэшированияСценарий 2: Публичный JSON-эндпоинт, который можно кэшировать на CDNСценарий 3: Ошибка авторизацииСценарий 4: Файлы и вложения — где часто путают типыПолевые заметки: как отлаживать проблемы с заголовками1) Всегда сверяйте «что реально ушло по wire»