Правильная настройка кэширования снижает нагрузку на сервер и ускоряет загрузку сайта. Браузер и CDN используют HTTP-заголовки, чтобы решить: сохранять ли ответ в кэш, как долго его хранить и когда запрашивать обновление. Неверные настройки приводят к устаревшему контенту или, наоборот, к лишним запросам.
Проверить текущие заголовки кэширования можно через HTTP Headers Analyzer и Web Vitals для оценки влияния на производительность.
Как работает HTTP-кэширование
- Сервер отдаёт ответ с заголовками
Cache-Control,ETag,Expires. - Браузер сохраняет ответ в кэш.
- При повторном запросе браузер проверяет: не истёк ли кэш.
- Если истёк — отправляет условный запрос с
If-None-Match(ETag) илиIf-Modified-Since. - Сервер отвечает 304 Not Modified или отдаёт новый контент.
Подробнее о всех HTTP-заголовках — в HTTP заголовки: полный справочник для разработчика.
Cache-Control: основные директивы
Время жизни кэша
Cache-Control: max-age=3600max-age — время в секундах, в течение которого ответ считается свежим. После истечения браузер может отправить условный запрос для валидации.
Публичный и приватный кэш
Cache-Control: public, max-age=86400| Значение | Описание |
|---|---|
| public | Можно кэшировать в CDN, прокси, браузере |
| private | Только в браузере пользователя (персональные данные) |
Запрет кэширования
Cache-Control: no-storeОтвет не сохраняется. Для чувствительных данных (банковские операции, персональная информация).
Cache-Control: no-cacheКэш можно хранить, но перед использованием нужна валидация у сервера (через ETag или Last-Modified).
CDN и shared cache
Cache-Control: public, max-age=3600, s-maxage=86400s-maxage — время жизни в shared cache (CDN, прокси). Браузер использует max-age.
Stale-while-revalidate
Cache-Control: max-age=3600, stale-while-revalidate=600Пока идёт повторная валидация (до 600 секунд), браузер может отдать устаревший контент. Улучшает perceived performance.
ETag и условные запросы
ETag — хеш или идентификатор версии контента. При повторном запросе браузер отправляет:
If-None-Match: "abc123"Если контент не изменился, сервер отвечает:
HTTP/1.1 304 Not Modified
ETag: "abc123"Тело ответа не передаётся — экономия трафика.
Слабый ETag
ETag: W/"abc123"W/ означает weak ETag — контент семантически эквивалентен, но байт-в-байт может отличаться (например, пробелы).
Last-Modified и If-Modified-Since
Альтернатива ETag — дата последнего изменения:
Last-Modified: Wed, 21 Oct 2025 07:28:00 GMTПри повторном запросе:
If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMTСервер сравнивает дату и при отсутствии изменений отдаёт 304.
Expires (устаревший механизм)
Expires: Thu, 31 Dec 2025 23:59:59 GMTПроблемы: зависимость от точности часов клиента, негибкость. Предпочтительно использовать Cache-Control: max-age.
Vary
Vary: Accept-Encoding, User-AgentУказывает, что ответ зависит от этих заголовков запроса. Кэш хранит отдельные варианты для разных комбинаций. Важно для контента, зависящего от языка, устройства или кодировки.
Стратегии по типам контента
Статические ресурсы с хешем в имени
Файлы вида main.a1b2c3d4.js, style.e5f6g7h8.css — при изменении контента меняется имя. Можно кэшировать надолго:
Cache-Control: public, max-age=31536000, immutableСтатические ресурсы без хеша
Cache-Control: public, max-age=86400, must-revalidateКороткий кэш с обязательной валидацией после истечения.
HTML-страницы
Cache-Control: no-cacheИли max-age=0, must-revalidate — всегда валидировать перед использованием. HTML часто содержит ссылки на актуальные версии JS/CSS.
API с персональными данными
Cache-Control: private, no-storeAPI с публичными данными
Cache-Control: public, max-age=60, stale-while-revalidate=300Настройка в Nginx
# Статика с хешем
location ~* \.[a-f0-9]{8,}\.(js|css)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
# Обычная статика
location /static/ {
add_header Cache-Control "public, max-age=86400, must-revalidate";
}
# HTML
location / {
add_header Cache-Control "no-cache";
}Настройка в Node.js (Express)
// Статика
app.use('/static', express.static('public', {
maxAge: '1y',
immutable: true,
etag: true,
lastModified: true
}));
// HTML
app.use((req, res, next) => {
if (req.path.endsWith('.html') || !req.path.includes('.')) {
res.setHeader('Cache-Control', 'no-cache');
}
next();
});Проверка кэширования
curl -I https://example.com/static/main.jsОжидаемые заголовки:
Cache-Control: public, max-age=31536000, immutable
ETag: "abc123"Онлайн: HTTP Headers — полный разбор заголовков ответа.
Типичные ошибки
- Долгий кэш для HTML без хешей в URL — пользователи видят старую версию после обновления.
- no-cache вместо no-store — для чувствительных данных нужен
no-store. - Игнорирование Vary — неправильный контент при разных Accept-Language или User-Agent.
- Кэш для авторизованных страниц — персональные данные могут утечь в shared cache.