📦 HTTP-заголовки и кэширование

💡 Основная идея

Вся ценность кэширования — в том, чтобы полностью пропустить сетевую цепочку из TCP & TLS Handshake, или хотя бы пропустить самую дорогую её часть — тело ответа.


🏷 Категории заголовков

  • General — не привязаны конкретно к запросу или ответу: Date, Connection (keep-alive), Cache-Control (используется в обоих).
  • Request — контекст запроса: Host, User-Agent, Accept, Authorization, If-None-Match.
  • Response — контекст ответа: Server, Set-Cookie, Location.
  • Entity/Representation — описывают тело независимо от направления: Content-Type, Content-Length, Content-Encoding, ETag, Last-Modified.

Разделение не формальное — это скорее подсказка, где заголовок вообще имеет смысл (Content-Length бессмыслен без тела).


🗄 Cache-Control

  • max-age=N — сколько секунд ответ считается свежим; в течение этого окна браузер мгновенно отдаёт кэшированную копию, вообще не обращаясь к серверу.
  • no-cache — обманчивое название: кэшировать можно, но при каждой отдаче требуется ревалидация с сервером через условный запрос (см. ETag ниже).
  • no-store — кэширование запрещено полностью, в любой форме.
  • private / public — можно кэшировать только в браузере пользователя / можно кэшировать в любом месте цепочки (включая CDN/прокси).

Ключевое отличие: no-cache = «кэшируй, но всегда ревалидируй»; no-store = «вообще не кэшируй».

ETag и условные запросы

  1. Первый ответ: 200 OK + тело + ETag: "abc123" (отпечаток конкретной версии ресурса).
  2. Кэшированная копия устарела / no-cache — браузер отправляет If-None-Match: "abc123" вместо слепой повторной загрузки.
  3. Сервер сравнивает: совпадение → 304 Not Modified, без тела (экономит весь трафик, который занимало бы тело ответа); нет совпадения → 200 OK + новое тело + новый ETag.

If-Modified-Since/Last-Modified работают по тому же принципу, но по времени, а не по хешу — грубее (точность до секунды), но не требует вычисления хеша на сервере.

В ответах 304 нет ничего, кроме заголовков — ни тела, ни дополнительной структуры.

Где кэширование находится в цепочке

Кэширование срабатывает до сети: если max-age ещё не истёк, соединение вообще не открывается (ни DNS, ни TCP, ни TLS). no-cache/304 — частичный выигрыш: сам round trip всё равно происходит, но экономится самая дорогая часть (тело ответа).


🌍 Места хранения кэша и стратегии

Три точки на пути запроса, каждая — потенциальный «склад» с готовой копией, от ближайшей/быстрейшей до самой дальней/медленной:

  1. Кэш браузера — копия на диске/в памяти пользователя. Сеть вообще не задействована.
  2. CDN — копия на сервере, географически близком к пользователю (не origin-сервер сайта). Требует сети, но путь короче. Используется только если ответ помечен Cache-Control: public; private разрешает копию только в кэше браузера, но не на CDN/общем прокси.
  3. Origin-сервер — место, где реально живут код и данные. Самый дальний вариант, к которому обращаются, только если копии взять больше негде.

Стратегии

  • cache-first — если что-то есть в кэше, отдать это и вообще не трогать сеть (без проверок, а не «постоянная ревалидация»). Безопасно для ресурсов, которые никогда не меняются под одним и тем же URL (хешированное имя файла — изменение порождает новое имя/URL, поэтому старая закэшированная копия никогда не может стать «неправильной»).
  • network-first — сначала пытаться получить свежую копию по сети; кэш — лишь запасной вариант на случай недоступности сети. Для часто меняющихся данных (лента, курс валют), где важна свежесть.
  • stale-while-revalidate — гибрид: сразу отдать то, что есть в кэше (даже если оно устарело), и параллельно, в фоне выполнить обычный сетевой запрос, чтобы обновить кэш для следующего раза. Пользователь никогда не ждёт сеть, но не всегда видит самую новую версию.

HTTP & Network TCP & TLS Handshake — что позволяет пропустить кэширование CORS — ещё один сетевой шлюз на стороне браузера browser