Кеширование — Cache-Control
Cache-Control— HTTP-заголовок, управляющий кешированием ресурсов в браузере и CDN: определяет срок хранения, условия инвалидации и доступность кеша для shared/private пользователей.
Зачем нужно
Правильное кеширование — один из наиболее эффективных способов ускорить повторные посещения: статические ресурсы (JS, CSS, изображения) загружаются мгновенно из кеша. Неправильное — пользователи получают устаревший контент или сервер получает лишнюю нагрузку.
Где используется
- Статические ресурсы (JS, CSS, шрифты, изображения) — долгосрочное кеширование
- HTML-страницы —
no-cacheили короткий TTL - API-ответы —
no-storeдля приватных данных илиs-maxageдля CDN - CDN-конфигурация (Cloudflare, Fastly, AWS CloudFront)
Основной контент
Директивы Cache-Control
# Для статики с cache-busting (хэш в имени файла)
Cache-Control: public, max-age=31536000, immutable
# public → можно кешировать в CDN
# max-age → 1 год в секундах
# immutable → браузер не проверяет обновления (используй с хэшами!)
# Для HTML страниц
Cache-Control: no-cache
# no-cache → кешировать, но проверять актуальность (ETag/Last-Modified)
# НЕ "не кешировать"! Это no-store
# Для приватных данных (API с сессией)
Cache-Control: private, no-store
# CDN кеш отдельно от браузера
Cache-Control: public, max-age=3600, s-maxage=86400
# max-age=3600 → браузер: 1 час
# s-maxage=86400 → CDN: 24 часа
Стратегии кеширования
1. Cache-busting (рекомендуется для JS/CSS)
/bundle.a3f8c2d.js → Cache-Control: max-age=31536000, immutable
При изменении → новый хэш → новый URL → новый кеш
2. Stale-While-Revalidate (для API)
Cache-Control: max-age=60, stale-while-revalidate=600
→ Отдаёт кеш сразу, обновляет в фоне
3. ETag + If-None-Match (для HTML)
ETag: "abc123"
→ Клиент: If-None-Match: "abc123"
→ Сервер: 304 Not Modified (тело не передаётся)
Express: настройка заголовков
const path = require('path');
// Статические файлы с хэшами → долгосрочный кеш
app.use('/static', express.static('dist', {
maxAge: '1 year',
immutable: true,
// Устанавливает: Cache-Control: public, max-age=31536000, immutable
}));
// HTML → no-cache
app.get('*', (req, res) => {
res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
// API: приватные данные
app.get('/api/profile', authenticate, (req, res) => {
res.set('Cache-Control', 'private, no-store');
res.json(req.user);
});
// API: публичные данные с CDN кешом
app.get('/api/products', (req, res) => {
res.set('Cache-Control', 'public, max-age=60, s-maxage=3600, stale-while-revalidate=86400');
res.json(products);
});
Service Worker: Cache API
// sw.js — кеш стратегия Cache First для статики
self.addEventListener('fetch', event => {
if (event.request.destination === 'script' || event.request.destination === 'style') {
event.respondWith(
caches.match(event.request).then(cached => {
return cached || fetch(event.request).then(response => {
const clone = response.clone();
caches.open('v1').then(cache => cache.put(event.request, clone));
return response;
});
})
);
}
});
Частые ошибки
no-cacheвместоno-storeдля чувствительных данных —no-cacheкеширует, но проверяетmax-age=31536000без хэша в имени файла — пользователи не получают обновления год- Кеширование персонализированных ответов с
public— CDN отдаёт чужие данные - Отсутствие
Vary: Accept-Encodingпри gzip/brotli — CDN кеширует некорректно
Связанные темы
- _MOC Производительность
- Сжатие -- gzip и brotli
- Edge Computing
- Метрики -- FCP, TTFB, TTI, TBT
- _MOC Сеть