Кэширование HTTP: строгий кэш, согласование и стратегии CDN

Веб/SEO

Кэш HTTP: первая линия защиты оптимизации производительности

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

Тип кэша Механизм Запрос к серверу? Скорость
Строгий кэш Cache-Control / Expires Нет Самая быстрая (0мс)
Кэш с согласованием ETag / Last-Modified Да (условный запрос) Быстрая (ответ 304)
Без кэша no-cache / no-store Да (полный запрос) Самая медленная

I. Строгий кэш

Директивы Cache-Control

Cache-Control: max-age=31536000, immutable
Директива Действие Применимый сценарий
max-age=N Кэшировать N секунд Статические ресурсы
s-maxage=N CDN кэширует N секунд (переопределяет max-age) Сценарии CDN
immutable Не перепроверять даже при истечении Имена файлов с хешем
public Разрешить промежуточный кэш CDN
private Только кэш браузера Персонализированный контент
no-cache Обязательно проверять перед каждым использованием HTML-страницы
no-store Полностью не кэшировать Конфиденциальные данные
must-revalidate Обязательно проверять после истечения Ответы API
stale-while-revalidate=N После истечения вернуть старое значение, обновить в фоновом режиме Прогрессивное обновление

Expires (старый стандарт)

Expires: Thu, 02 Jun 2027 00:00:00 GMT

Приоритет: Cache-Control: max-age > Expires.


II. Кэш с согласованием

ETag / If-None-Match

# Первый запрос
GET /api/data
→ 200 OK
  ETag: "abc123"
  Cache-Control: max-age=0

# Повторный запрос (условный)
GET /api/data
If-None-Match: "abc123"
→ 304 Not Modified  (без тела ответа, экономит полосу пропускания)

Last-Modified / If-Modified-Since

# Первый запрос
GET /style.css
→ 200 OK
  Last-Modified: Mon, 01 Jun 2026 12:00:00 GMT

# Повторный запрос
GET /style.css
If-Modified-Since: Mon, 01 Jun 2026 12:00:00 GMT
→ 304 Not Modified

ETag vs Last-Modified

Измерение ETag Last-Modified
Точность Байтовый уровень Секундный уровень
Производительность Требует вычисления хеша Прямое чтение времени файла
Надёжность Высокая Низкая (множественные изменения за 1 секунду теряются)
Распределённый Необходимо обеспечить согласованность хеша Естественно согласован

Рекомендация: Приоритет ETag.


III. Матрица стратегий кэширования

HTML-страницы

Cache-Control: no-cache

HTML — входной файл, должен проверяться каждый раз для обеспечения пользователей последней версией.

Статические ресурсы с хешем

Cache-Control: max-age=31536000, immutable

Имя файла содержит хеш (напр., app.abc123.js), при изменении содержимого хеш меняется → URL меняется → новый запрос. Старый URL никогда не меняется, можно кэшировать на год и пометить immutable.

Статические ресурсы без хеша

Cache-Control: max-age=86400, stale-while-revalidate=604800

После истечения сначала возвращается старое значение, фоновое обновление происходит незаметно для пользователя.

Ответы API

Cache-Control: private, max-age=0, must-revalidate

Ответы API обычно содержат пользовательские данные, private запрещает кэширование CDN, must-revalidate гарантирует, что после истечения старые значения не возвращаются.


IV. Заголовок Vary: расширение ключа кэша

Vary: Accept-Encoding, Accept-Language

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

Значение Vary Значение Сценарий
Accept-Encoding Кэшировать по методу сжатия gzip vs br
Accept-Language Кэшировать по языку Многоязычные сайты
Cookie Кэшировать по Cookie Персонализированные страницы
* Каждый запрос с отдельным кэшем Эквивалент отсутствию кэша

Внимание: Vary: Cookie приводит к крайне низкой частоте попадания в кэш, используйте с осторожностью.


V. Стратегия кэширования CDN

Управление обращением к источнику

# CDN кэширует 1 день, браузер кэширует 5 минут
Cache-Control: public, s-maxage=86400, max-age=300
Сценарий s-maxage max-age Описание
Статические ресурсы 31536000 31536000 CDN и браузер с длительным кэшем
Ответы API 300 0 CDN кэширует 5 минут, браузер не кэширует
HTML 60 0 CDN кэширует 1 минуту, браузер не кэширует
Персонализированный контент — 0 CDN не кэширует, браузер не кэширует

Обновление кэша CDN

Метод Скорость Применимо
Предварительный прогрев URL Быстро Публикация нового контента
Полное обновление Медленно Срочное исправление
Обновление каталога Средне Пакетное обновление
Cache-Control: no-cache Автоматически Сценарии API

Stale-While-Revalidate на практике

Cache-Control: max-age=60, stale-while-revalidate=86400
Секунда 0: Запрос → Кэш MISS → Обращение к источнику → Возврат свежего значения
Секунда 30: Запрос → Кэш HIT → Возврат кэшированного значения
Секунда 90: Запрос → Кэш STALE → Немедленный возврат старого значения + фоновое обращение к источнику
Секунда 91: Обращение к источнику завершено → Кэш обновлён
Секунда 120: Запрос → Кэш HIT → Возврат нового значения

Эффект: Пользователь никогда не ждёт обращения к источнику, тихое обновление в фоновом режиме.


VI. Стратегия инвалидации кэша

Хеш содержимого (рекомендуется)

<!-- Хеш генерируется при сборке -->
<script src="/app.abc123.js"></script>
<link href="/style.def456.css">
// Конфигурация webpack
output: {
  filename: '[name].[contenthash:8].js',
}

Параметр запроса версии

<!-- Просто, но CDN может не кэшировать параметры ?v= -->
<script src="/app.js?v=2.0.0"></script>

Проблема: Некоторые CDN игнорируют параметры запроса, что приводит к необновлению кэша.

Метка времени (не рекомендуется)

<!-- Меняется каждый раз, полностью разрушает кэш -->
<script src="/app.js?t=1717315200"></script>

VII. Кэш Service Worker

Стратегия Cache First

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then(cached => cached || fetch(event.request))
  );
});

Стратегия Stale While Revalidate

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open('dynamic').then(cache =>
      cache.match(event.request).then(cached => {
        const fetchPromise = fetch(event.request)
          .then(response => {
            cache.put(event.request, response.clone());
            return response;
          });
        return cached || fetchPromise;
      })
    )
  );
});

Выбор стратегии кэширования

Стратегия Приоритетный источник Применимо
Cache First Кэш Шрифты, изображения
Network First Сеть API, HTML
Stale While Revalidate Кэш + фоновое обновление CSS, JS
Network Only Сеть Не-GET запросы
Cache Only Кэш Офлайн-страницы

VIII. Отладка кэша

Chrome DevTools

  1. DevTools → Панель Network
  2. Проверить столбец Size:
    • from disk cache: Попадание в дисковый кэш
    • from memory cache: Попадание в кэш памяти
    • Фактический размер: Промах
  3. Проверить заголовок ответа Age: Время нахождения в кэше
  4. Отметить Disable cache: Отключить кэш (когда DevTools открыт)

Проверка с curl

# Посмотреть заголовки ответа
curl -I https://example.com/style.css

# Отправить условный запрос
curl -I -H 'If-None-Match: "abc123"' https://example.com/style.css
# 304 означает попадание в кэш с согласованием

# Имитировать другой Accept-Encoding
curl -I -H 'Accept-Encoding: br' https://example.com/style.css

IX. Контрольный список лучших практик

  1. HTML: no-cache, обеспечить актуальность входного файла
  2. JS/CSS с хешем: max-age=1 год + immutable
  3. API: must-revalidate, обязательно проверять после истечения
  4. CDN: s-maxage для контроля частоты обращения к источнику
  5. Vary: Accept-Encoding, различать методы сжатия
  6. Использовать ETag вместо Last-Modified
  7. stale-while-revalidate для незаметного обновления
  8. Service Worker для дополнения офлайн-возможностей
#HTTP缓存#Cache-Control#ETag#CDN#性能优化