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-заголовок, стандартизований в HTTP/1.1 (RFC 7234), який дозволяє серверу вказати, як і протягом якого часу клієнти, проксі та CDN можуть кешувати відповідь. На відміну від Expires (HTTP/1.0), Cache-Control використовує директиви — текстові команди, що комбінуються через кому: Cache-Control: public, max-age=3600, must-revalidate. Заголовок надає тонкий контроль над кожною ланкою ланцюжка кешування.
Кешування — один із фундаментальних механізмів продуктивності веб-та мобільних додатків. Без нього кожен запит користувача йшов би безпосередньо до сервера, викликаючи надмірне навантаження та затримки. Cache-Control визначає три рівні кешування: браузер/додаток (приватний кеш), проксі-сервери (спільний кеш) та CDN (розподілений кеш). Кожен рівень інтерпретує директиви по-різному.
Неправильне налаштування 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 включає понад 10 директив, розділених на три групи: директиви запиту (клієнт → сервер), директиви відповіді (сервер → клієнт) та розширення. На практиці в мобільній розробці використовуються 6-7 основних директив відповіді, що охоплюють 95% сценаріїв кешування. Розглянемо кожну з прикладами та рекомендаціями.
| Директива | Значення | Приклад |
|---|---|---|
| max-age | Час життя в секундах від моменту відповіді | max-age=3600 — 1 година |
| s-maxage | max-age для спільного кешу (проксі, 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 обов’язково перевіряти на джерелі | max-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 не забороняє кешування — він вимагає перевіряти кешовану копію при кожному використанні через умовний запит (If-Modified-Since або If-None-Match). Якщо сервер відповідає 304 — клієнт використовує кеш. Якщо 200 — оновлює. no-store ж повністю забороняє зберігати відповідь в будь-якому кеші, включно з диском та оперативною пам’яттю. Використовуйте no-store лише для чутливих даних — токени, платіжні дані, особисті документи.
Заголовок 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: 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)).
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 хвилин). Інтерцептор видаляє застарілий заголовок Pragma (HTTP/1.0) для сумісності. Кешування на iOS працює аналогічно через URLCache.shared з налаштуваннями memoryCapacity та diskCapacity.
Директива stale-while-revalidate дозволяє показувати користувачу застарілий кеш, поки додаток у фоні завантажує свіжі дані. Це дає ефект миттєвої відповіді: користувач бачить контент відразу, а через секунду він оновлюється до актуальної версії. Підтримується OkHttp починаючи з версії 3.10 та URLCache на iOS 14+. Приклад: Cache-Control: max-age=3600, stale-while-revalidate=300 — 1 година свіжого кешу, потім 5 хвилин показу застарілих даних з фоновим оновленням.
Різні типи ресурсів потребують різних стратегій кешування. Розглянемо оптимальні конфігурації для типових сценаріїв у мобільній розробці. Для статичного контенту з хешем у назві файлу (bundle.abc123.js) можна встановлювати max-age до 1 року з immutable. Для списків API, які рідко оновлюються (довідники, категорії), — max-age від 5 хвилин до 1 години з stale-while-revalidate.
| Тип ресурсу | Cache-Control | Пояснення |
|---|---|---|
| Версіоновані статичні ресурси | public, max-age=31536000, immutable | 1 рік, файли не змінюються (хеш в URL) |
| Неверсіоновані статичні ресурси | public, max-age=86400, must-revalidate | 1 день з примусовою перевіркою після |
| API: довідкові дані | public, max-age=600, stale-while-revalidate=60 | 10 хвилин кешу + 1 хвилина застарілих даних |
| API: дані користувача | private, max-age=60 | 1 хвилина, лише для конкретного користувача |
| API: чутливі дані | no-store | Повна заборона кешування |
| HTML-сторінки | no-cache, must-revalidate | Перевірка при кожному запиті, 304 при незмінності |
Важливо пам’ятати про безпеку: для відповідей, що містять персональні дані користувача, завжди встановлюйте private. Без цієї директиви публічний проксі (наприклад, корпоративний) може закешувати відповідь та надати їі іншому користувачу. Для токенів автентифікації та платіжної інформації використовуйте no-store — навіть приватний кеш не повинен зберігати ці дані на диску.
Для перевірки правильності 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 — лише для спільних кешів (проксі, CDN). Якщо вказано s-maxage, CDN ігнорує max-age та використовує s-maxage. Це дозволяє задати різний час життя для браузера та CDN.
Ні, після відправлення відповіді з max-age клієнт не робитиме запит до закінчення таймера. Для негайної інвалідації кешу потрібно змінити URL ресурсу (додати версію/хеш) та розіслати push-повідомлення або WebSocket-повідомлення для примусового скидання.
Директива immutable (RFC 8246) повідомляє браузеру, що ресурс ніколи не зміниться за цією URL. Браузер навіть не намагається зробити умовний запит при оновленні сторінки — він використовує кеш до закінчення max-age. Працює лише з версіонованими файлами.
Googlebot враховує Cache-Control: довге кешування прискорює повторне сканування. noindex з швидким кешем нормально. no-store може уповільнити індексацію, оскільки Googlebot буде завантажувати сторінку з нуля кожного разу. Занадто короткий max-age збільшує навантаження сервера під час сканування.
Через helmet або middleware: res.set('Cache-Control', 'public, max-age=3600'). Для статичних файлів використовуйте express.static з параметром maxAge: express.static('public', {maxAge: '1y'}). Для динамічних маршрутів — індивідуально в кожному обробнику.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також