Cache-Control — что это такое, директивы и управление кэшированием

Автор: IT Sectr Опубликовано: 2026-03-09 Время чтения: 9 мин

Cache-Control — HTTP-заголовок, который определяет правила кэширования ресурсов на стороне клиента, прокси-серверов и CDN с помощью набора директив. В отличие от устаревшего заголовка Expires, Cache-Control поддерживает десятки комбинаций: max-age задаёт время жизни в секундах, private и public управляют доступностью кэша, no-cache и no-store — принудительной проверкой. По данным Google Web Dev (2025), правильная конфигурация Cache-Control может сократить время загрузки страниц на 50-80% для повторных посещений. Это делает заголовок критически важным для производительности веб- и мобильных приложений.

Главное

  • Cache-Control — HTTP-заголовок с директивами, управляющими кэшированием на клиенте, прокси и CDN
  • max-age — ключевая директива, задающая время жизни ресурса в секундах без повторной проверки
  • private vs public — private разрешает кэш только на клиенте, public — также на прокси и CDN
  • no-cache vs no-store — no-cache требует проверки перед использованием, no-store запрещает кэш полностью
  • s-maxage — переопределяет max-age для общих (shared) кэшей, не влияя на браузеры

Что такое Cache-Control?

Cache-Control — это HTTP-заголовок, стандартизированный в HTTP/1.1 (RFC 7234), который позволяет серверу указать, как и в течение какого времени клиенты, прокси и CDN могут кэшировать ответ. В отличие от Expires (HTTP/1.0), Cache-Control использует директивы — текстовые команды, которые комбинируются через запятую: Cache-Control: public, max-age=3600, must-revalidate. Заголовок даёт тонкий контроль над каждым звеном цепочки кэширования.

Кэширование — один из фундаментальных механизмов производительности веба и мобильных приложений. Без него каждый запрос пользователя шёл бы напрямую к серверу, вызывая избыточную нагрузку и задержки. Cache-Control определяет три уровня кэширования: браузер/приложение (private cache), прокси-серверы (shared cache) и CDN (distributed cache). Каждый уровень интерпретирует директивы по-своему.

Неправильная настройка Cache-Control — одна из самых частых причин проблем с производительностью. Слишком агрессивное кэширование приводит к тому, что пользователи видят устаревшие данные. Слишком слабое — к избыточным запросам к серверу и медленной загрузке. По данным Akamai (2025), оптимизация Cache-Control для статического контента снижает нагрузку на сервер на 70-90% и улучшает время загрузки на 40-60% для мобильных пользователей.

История заголовка

Cache-Control появился в HTTP/1.1 (RFC 2616, 1999) как замена Expires. Expires имел фундаментальную проблему: он использовал абсолютную дату, которая зависела от часового пояса сервера и клиента. Cache-Control решил эту проблему, перейдя на относительное время (max-age в секундах от момента получения ответа). Позже в RFC 7234 (2014) были добавлены новые директивы: immutable для статики, stale-while-revalidate и stale-if-error для отложенной проверки.

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

Cache-Control включает более 10 директив, разделённых на три группы: директивы запроса (клиент → сервер), директивы ответа (сервер → клиент) и расширения. На практике в мобильной разработке используются 6-7 основных директив ответа, покрывающих 95% сценариев кэширования. Рассмотрим каждую с примерами и рекомендациями.

ДирективаЗначениеПример
max-ageВремя жизни в секундах с момента ответаmax-age=3600 — 1 час
s-maxagemax-age для shared cache (прокси, CDN)s-maxage=86400 — 1 день для CDN
publicРазрешает кэширование всем (включая прокси)public, max-age=3600
privateРазрешает кэш только браузеру/приложениюprivate, max-age=600
no-cacheНе использовать без проверки (304 обязателен)no-cache
no-storeЗапрет кэширования полностьюno-store
must-revalidateПосле max-age обязательно проверять у originmax-age=3600, must-revalidate
immutableРесурс не изменится (для версионированной статики)max-age=31536000, immutable

max-age — самая важная директива. Она запрещает клиенту делать запрос к серверу в течение указанного времени. Для статики (CSS, JS, изображения) max-age обычно устанавливают от 1 дня до 1 года. Для API-ответов — от 0 секунд (данные всегда свежие) до 5-10 минут (справочные данные). s-maxage позволяет задать разное время жизни для CDN и браузера: CDN хранит копию 1 день, браузер — 1 час.

no-cache vs no-store

Эти две директивы часто путают. no-cache не запрещает кэширование — он требует проверять закэшированную копию при каждом использовании через условный запрос (If-Modified-Since или If-None-Match). Если сервер отвечает 304 — клиент использует кэш. Если 200 — обновляет. no-store же полностью запрещает сохранять ответ в любом кэше, включая дисковый и оперативный. Используйте no-store только для чувствительных данных — токены, платёжные данные, персональные документы.

Cache-Control vs Expires

Заголовок Expires (HTTP/1.0) также указывает время жизни ресурса, но использует абсолютную дату: Expires: Thu, 03 Jul 2026 12:00:00 GMT. Cache-Control max-age — относительное время от момента ответа. Разница критична для распределённых систем: если сервер и клиент находятся в разных часовых поясах, Expires может быть интерпретирован неверно. Cache-Control не имеет этой проблемы — 3600 секунд всегда 3600 секунд.

Когда оба заголовка присутствуют, Cache-Control имеет приоритет над Expires. Это определено в RFC 7234: «Если ответ содержит Cache-Control с директивой max-age, получатель ДОЛЖЕН игнорировать Expires». На практике рекомендуется не возвращать Expires вообще для современных клиентов, так как Cache-Control покрывает все сценарии Expires. Однако для обратной совместимости со старыми прокси и браузерами можно возвращать оба заголовка.

Expires сохранился в основном для статического контента на Nginx и Apache — эти серверы автоматически добавляют оба заголовка. Если в вашем проекте встречается Expires без Cache-Control, замените его на Cache-Control с max-age: точность управления кэшем повышается, а зависимость от часового пояса устраняется. Для миграции достаточно настроить сервер на добавление Cache-Control вместо Expires.

nginx
# Nginx: Cache-Control для статических файлов
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 30d;
    add_header Cache-Control "public, immutable, max-age=2592000";
}

# Разные политики для разных типов контента
location /api/config {
    expires -1;
    add_header Cache-Control "no-cache, must-revalidate";
}

location /api/static-data {
    expires 5m;
    add_header Cache-Control "public, max-age=300";
}

В конфигурации Nginx статическим файлам (CSS, JS, изображения) устанавливается Cache-Control на 30 дней с атрибутом immutable — этот атрибут сообщает браузеру, что ресурс никогда не меняется по этому URL (версионирование через хеш в имени файла). API-эндпоинты используют no-cache для динамических данных и public с коротким max-age для справочных — часто запрашиваемые и редко меняющиеся списки.

Кэширование в мобильных приложениях

В мобильных приложениях Cache-Control играет особую роль из-за ограничений мобильных сетей: высокая задержка, нестабильное соединение, лимиты трафика. Правильное кэширование позволяет показывать пользователю данные мгновенно, даже офлайн, и обновлять их в фоне. OkHttp на Android и URLSession на iOS имеют встроенные системы кэширования, учитывающие Cache-Control.

OkHttp использует CacheInterceptor, который читает Cache-Control из ответа и автоматически управляет кэшированием. Если сервер вернул Cache-Control: max-age=3600, OkHttp не будет делать запрос к серверу в течение часа. После истечения max-age OkHttp отправляет условный запрос с If-Modified-Since и If-None-Match. Настройка кэша в OkHttp: OkHttpClient.Builder().cache(Cache(directory, maxSize)).

kotlin
fun createCachedClient(cacheDir: File): OkHttpClient {
    return OkHttpClient.Builder()
        .cache(Cache(cacheDir, 10L * 1024 * 1024))
        .addNetworkInterceptor { chain ->
            val response = chain.proceed(chain.request())
            response.newBuilder()
                .header("Cache-Control",
                    "public, max-age=300")
                .removeHeader("Pragma")
                .build()
        }
        .build()
}

Код создаёт OkHttpClient с кэшем на 10 МБ и переопределяет Cache-Control через NetworkInterceptor. Если сервер не возвращает Cache-Control или использует Expires, интерцептор добавляет public, max-age=300 (5 минут). interceptor удаляет устаревший заголовок Pragma (HTTP/1.0) для совместимости. По аналогичной схеме работает кэширование на iOS через URLCache.shared с настройкой memoryCapacity и diskCapacity.

Офлайн-режим и stale-while-revalidate

Директива stale-while-revalidate позволяет показывать пользователю устаревший кэш (stale), пока приложение в фоне загружает свежие данные. Это даёт эффект мгновенного отклика: пользователь видит контент сразу, а через секунду он обновляется до актуального. Поддерживается OkHttp начиная с версии 3.10 и URLCache на iOS 14+. Пример: Cache-Control: max-age=3600, stale-while-revalidate=300 — 1 час актуального кэша, потом 5 минут показа устаревшего с фоновым обновлением.

Примеры настройки Cache-Control

Разные типы ресурсов требуют разных стратегий кэширования. Рассмотрим оптимальные конфигурации для типовых сценариев в мобильной разработке. Для статического контента с хешем в имени файла (bundle.abc123.js) можно устанавливать max-age до 1 года с immutable. Для API-списков, которые обновляются редко (справочники, категории), — max-age от 5 минут до 1 часа с stale-while-revalidate.

Тип ресурсаCache-ControlПояснение
Версионированная статикаpublic, max-age=31536000, immutable1 год, файлы не меняются (хеш в URL)
Неверсионированная статикаpublic, max-age=86400, must-revalidate1 день с принудительной проверкой после
API: справочные данныеpublic, max-age=600, stale-while-revalidate=6010 минут кэш + 1 минута stale
API: пользовательские данныеprivate, max-age=601 минута, только для конкретного пользователя
API: чувствительные данныеno-storeПолный запрет кэширования
HTML-страницыno-cache, must-revalidateПроверка при каждом запросе, 304 при неизменности

Важно помнить о безопасности: для ответов, содержащих персональные данные пользователя, всегда устанавливайте private. Без этой директивы публичный прокси (например, корпоративный) может закэшировать ответ и отдать его другому пользователю. Для токенов аутентификации и платёжной информации используйте no-store — даже private кэш не должен сохранять эти данные на диск.

Отладка кэширования

Для проверки корректности Cache-Control используйте заголовок Age (сколько секунд кэш хранится) и X-Cache (hit/miss на CDN). В браузере — вкладка Network, колонка Size показывает «from disk cache» или «304 Not Modified». Если ресурс должен кэшироваться, но загружается каждый раз — проверьте, не добавляет ли сервер Cache-Control: no-cache или Pragma: no-cache вместе с вашими директивами.

Часто задаваемые вопросы

В чём разница между max-age и s-maxage?

max-age действует для всех кэшей (включая браузеры), s-maxage — только для shared cache (прокси, CDN). Если указан s-maxage, CDN игнорирует max-age и использует s-maxage. Это позволяет задать разное время жизни для браузера и CDN.

Можно ли отменить кэширование после отправки Cache-Control?

Нет, после отправки ответа с max-age клиент не будет делать запрос до истечения таймера. Для немедленной инвалидации кэша нужно изменить URL ресурса (добавить версию/хеш) и разослать push-уведомления или WebSocket-сообщения для принудительного сброса.

Что такое immutable директива?

Директива immutable (RFC 8246) сообщает браузеру, что ресурс никогда не изменится по данному URL. Браузер даже не пытается сделать условный запрос при обновлении страницы — использует кэш до истечения max-age. Работает только с версионированными файлами.

Как Cache-Control влияет на SEO?

Googlebot учитывает Cache-Control: долгое кэширование ускоряет повторное сканирование. noindex с fast-кэшем — ок. no-store может замедлить индексацию, так как Googlebot будет загружать страницу каждый раз с нуля. Слишком короткий max-age увеличивает нагрузку на сервер при сканировании.

Как настроить Cache-Control в Express.js?

Через helmet или middleware: res.set('Cache-Control', 'public, max-age=3600'). Для статики используйте express.static с параметром maxAge: express.static('public', {maxAge: '1y'}). Для динамических маршрутов — индивидуально в каждом обработчике.

Итоги

  • Cache-Control — основной HTTP-заголовок управления кэшированием с гибкой системой директив
  • max-age — время жизни в секундах от момента ответа; ключевая директива для всех сценариев кэширования
  • private vs public — private только для клиента, public для прокси и CDN; влияет на безопасность данных
  • no-cache требует проверки, no-store запрещает кэш полностью; разные назначения, не путать
  • s-maxage — переопределяет max-age для shared cache, полезен для разделения политик браузер/CDN
  • stale-while-revalidate — показ устаревшего кэша с фоновым обновлением для мгновенного UX
  • Рекомендация — настроить Cache-Control для каждого типа ресурсов на сервере и в мобильном HTTP-клиенте

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также