Правильная настройка кэширования снижает нагрузку на сервер и ускоряет загрузку сайта. Браузер и 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=3600
max-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=86400
s-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-store
API с публичными данными
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.