HTTP-заголовки кэширования (Cache-Control) 🚀 — подробный обзор
Представьте, что вы каждое утро приходите в одно и то же кафе и спрашиваете бариста: «Какой кофе вы варите?». Он перечисляет все меню. На следующее утро вы снова спрашиваете то же, и он снова перечисляет. Это пустая трата времени, правда? Логичнее было бы запомнить тот небольшой перечень доступного кофе и спрашивать его только тогда, когда он изменится.
Именно так работает кэширование в браузере. Без него ваш браузер при каждом посещении страницы вынужден заново загружать каждую картинку, скрипт и стиль. Это медленно и дорого. HTTP-заголовки кэширования — это инструкции, которые сервер дает браузеру: «Запомни этот файл на неделю» или «Проверяй этот файл при каждом визите».
В этой статье я расскажу о том, как правильно настроить эти инструкции, чтобы ваш сайт летал, а сервер не «задыхался» от лишних запросов.
Главный игрок: Cache-Control
Раньше для кэширования использовали заголовок Expires, но сегодня стандартом де-факто является Cache-Control. Это мощный инструмент, который позволяет очень гибко управлять поведением кэша.
Он состоит из директив. Давайте разберем самые важные из них:
| Директива | Что она означает |
|---|---|
max-age=3600 |
Файл считается «свежим» в течение указанного количества секунд (здесь — 1 час). Браузер даже не будет спрашивать сервер об этом файле, пока время не истечет. |
public |
Файл может кэшироваться кем угодно: браузером, CDN, прокси-сервером. Идеально для статики. |
private |
Файл предназначен для одного пользователя (например, страница профиля). Кэшировать может только браузер, но не CDN. |
no-cache |
Внимание, ловушка! Это не значит «не кэшировать». Это значит «кэшируй, но перед каждым использованием спрашивай сервер, не изменился ли файл» (валидация). |
no-store |
Вот это действительно «не кэшировать нигде и никогда». Используется для банковских данных и конфиденциальной информации. |
must-revalidate |
Когда срок max-age истечет, браузер обязан проверить актуальность файла на сервере, а не использовать старую копию. |
Как браузер узнает, что файл изменился? (Валидаторы)
Когда вы используете no-cache или когда истекает время max-age, браузер делает запрос к серверу: «У меня есть картинка, вот ее метка. Она все еще актуальна?». Для этого существуют два заголовка-валидатора:
- ETag (Entity Tag): Уникальный хеш-код файла (например,
"33a64df551425fcc55e4d42a148795d9f25f89d4"). Если хеш на сервере совпадает с тем, что есть у браузера, сервер отвечает304 Not Modified(тело файла не передается). - Last-Modified: Дата последнего изменения файла. Менее точный, чем ETag, но тоже популярный.
Практика: как настроить кэширование
Настройка зависит от того, какой веб-сервер вы используете. Если вы не уверены, советую прочитать мою статью о разнице между Nginx и Apache.
Вариант 1: Nginx (Самый эффективный)
Добавьте этот код в блок server вашего конфигурационного файла:
# Кэширование статики (картинки, шрифты) на 1 год
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
expires 365d;
add_header Cache-Control "public, no-transform";
}
# Кэширование HTML (не кэшируем или кэшируем с проверкой)
location ~* \.(html)$ {
add_header Cache-Control "no-cache";
}
Вариант 2: Apache (.htaccess)
Если вы используете Apache, добавьте этот код в файл .htaccess в корне сайта. Вам понадобится модуль mod_expires.
<IfModule mod_expires.c>
ExpiresActive On
# картинки
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
# CSS и JS
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
# По умолчанию
ExpiresDefault "access plus 2 days"
</IfModule>
# Дополнительный контроль заголовков
<IfModule mod_headers.c>
<FilesMatch "\.(js|css|xml|gz|html)$">
Header append Vary: Accept-Encoding
</FilesMatch>
</IfModule>
Вариант 3: PHP (для динамического контента)
Иногда нужно запретить кэширование конкретной динамической страницы (например, корзины). Это можно сделать прямо в коде:
<?php
// Запрещаем кэширование
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
?>
Распространенные ошибки ⚠️
- Кэширование часто меняющихся файлов без версионирования. Если вы поставите
max-age=1 годнаstyle.cssи измените дизайн, пользователи будут видеть старый дизайн еще год. Решение: меняйте имя файла при обновлении (например,style.v2.cssилиstyle.css?v=2). - Использование
publicдля приватных данных. Никогда не используйтеpublicдля страниц, где есть личные данные пользователя, иначе они могут остаться в кэше публичного прокси. - Отсутствие заголовков. Если вы не пошлете никаких заголовков, браузер сам попытается «угадать», как кэшировать контент (Heuristic Caching), и результат может быть непредсказуемым.
Вывод: баланс между скоростью и актуальностью
По моему мнению, настройка заголовков кэширования — это искусство найти баланс. Вы хотите, чтобы пользователь загружал как можно меньше данных (долгий кэш), но при этом всегда видел актуальную версию сайта.
Мой рецепт прост: «агрессивно» кэшируйте статику (картинки, шрифты) с долгим сроком жизни, используйте версионирование файлов для CSS/JS, и будьте осторожны с кэшированием HTML-страниц, используя no-cache для динамического контента.