Кеш экономит сеть и время: ответ берётся из браузера, CDN или прокси, а не с сервера. Управляет этим сервер через заголовки ответа. Кешируются в основном ответы на GET.
Два режима: свежий и устаревший
Пока ответ свежий (fresh), браузер берёт его из кеша вообще без запроса. Когда он устарел (stale), браузер отправляет условный запрос: «у меня такая версия, она ещё годится?». Если да, сервер отвечает 304 Not Modified без тела, и браузер продлевает старую копию.
Cache-Control
Главный заголовок. Основные директивы:
max-age=3600- ответ свежий 3600 секунд.s-maxage=600- то же, но только для общих кешей (CDN, прокси), перекрываетmax-ageдля них.no-cache- хранить можно, но перед каждым использованием спросить сервер (ревалидация).no-store- не сохранять вообще. Для персональных и чувствительных данных.private- только браузер пользователя, CDN хранить нельзя.public- можно и общим кешам.immutable- ресурс никогда не изменится, не ревалидировать даже при перезагрузке.must-revalidate- устаревшую копию нельзя отдавать без проверки.stale-while-revalidate=60- 60 секунд после устаревания можно отдать старую копию и обновить её в фоне.
Самая частая путаница: no-cache не значит «не кешировать». Это значит «всегда проверяй». «Не кешировать» это no-store.
Expires с датой - устаревший предшественник max-age. Если есть оба, побеждает max-age.
Валидаторы: ETag и Last-Modified
HTTP/1.1 200 OK
Cache-Control: no-cache
ETag: "a1b2c3"
Last-Modified: Tue, 13 Oct 2026 10:00:00 GMT
Повторный запрос:
GET /api/profile
If-None-Match: "a1b2c3"
If-Modified-Since: Tue, 13 Oct 2026 10:00:00 GMT
Ответ 304 Not Modified, если версия та же. ETag - идентификатор версии (часто хеш содержимого), точнее даты: дата имеет точность до секунды. Если пришли оба условия, сервер опирается на If-None-Match.
ETag ещё помогает против потерянных обновлений: PUT с If-Match: "a1b2c3" выполнится, только если ресурс не менялся, иначе 412 Precondition Failed.
Vary
Vary: Accept-Encoding говорит кешу: храни отдельные копии для разных значений этого заголовка запроса. Без него CDN может отдать gzip-версию клиенту, который gzip не понимает, или русскую страницу англоязычному пользователю при Accept-Language.
Стратегия для фронтенда
index.html Cache-Control: no-cache
/assets/app.4f9a1c.js Cache-Control: public, max-age=31536000, immutable
Статика с хешем в имени кешируется на год: новый билд даёт новое имя, старый файл просто перестаёт запрашиваться. HTML всегда ревалидируется, чтобы пользователь получил ссылки на новые бандлы. Это называется cache busting.
Инвалидация в CDN
Если файл кешируется в CDN без хеша в имени, после деплоя его надо явно сбросить (purge) или ждать истечения s-maxage. Поэтому хеш в имени надёжнее.
Частые ошибки
no-cacheвместоno-storeдля приватных данных.- Долгий
max-ageнаindex.html: пользователи неделями сидят на старой версии. - Нет
privateна персональном ответе, и CDN отдаёт чужой профиль другим. - Забыли
Varyпри разных ответах на один URL.
Что спросят на собесе
- Разница
no-cacheиno-store. - Как работают
ETagи304. - Как кешировать JS-бандлы и
index.htmlи почему по-разному. - Что такое
Varyи когда без него будет баг. - Чем
privateотличается отpublic.