Как настроить кэш браузера на сайте

Кэш браузера хранит копии картинок, CSS, JavaScript и шрифтов. Сервер задает правила через заголовки ответа, поэтому посетитель повторно использует статические файлы без новой загрузки. Статья предназначена владельцам и разработчикам сайтов, а не пользователям, которым нужно очистить кэш. Вы настроите Apache или nginx, проверите результат и организуете обновление файлов через новые имена.

Что такое кэш браузера и зачем его настраивать

Браузер получает файл вместе с правилами его хранения. Он сравнивает срок свежести ресурса с текущим временем и решает, использовать копию или обратиться к серверу.

Схема повторного использования ресурсов из кэша браузера

При повторном открытии страницы браузер берет свежую картинку или таблицу стилей из локального кэша. Серверу не приходится передавать эти данные снова, поэтому сокращаются сетевые запросы и объем трафика.

После окончания срока свежести браузер может отправить условный запрос. Если файл не изменился, сервер вернет ответ 304 без тела ресурса. При изменении файла сервер передаст новую копию.

Кэширование ускоряет повторные посещения и переходы между страницами.

Повторное использование ресурсов может улучшить показатели Core Web Vitals, включая скорость отображения крупного элемента страницы. Результат зависит от того, какие ресурсы браузер уже сохранил. При первом посещении локальный кэш еще не помогает загрузить эти файлы.

Заголовки кэширования: Cache-Control, Expires, ETag, Last-Modified

Правила кэширования передает сервер в заголовках ответа. Браузер отправляет собственные заголовки запроса, но они не заменяют политику сервера.

ЗаголовокЧто делаетПример значения
Cache-ControlЗадает срок свежести и условия хранения ответаpublic, max-age=2592000
ExpiresУказывает момент окончания свежестиHTTP-дата в GMT
ETagПередает идентификатор версии ресурса"asset-a81c"
Last-ModifiedПередает дату последнего измененияHTTP-дата в GMT

Если ответ содержит Cache-Control: max-age и Expires, браузер определяет срок свежести по max-age.

Значения двух заголовков согласуйте между собой. Заголовки ETag и Last-Modified решают другую задачу: помогают проверить, изменился ли сохраненный ресурс.

Директивы Cache-Control

Директивы разделяйте запятыми. Для версионированного общего файла подойдет сочетание public, max-age=31536000, immutable.

ДирективаПоведениеКогда применять
max-ageЗадает свежесть в секундах2592000 – 30 дней; 31536000 – год
publicРазрешает хранение в общих кэшахПубличные картинки, CSS и JavaScript
privateОставляет хранение только в личном кэшеПерсональный ответ без чувствительных данных
no-cacheРазрешает хранение, но требует перепроверки перед использованиемПубличный HTML, который часто меняется
no-storeЗапрещает сохранение ответаЧувствительные персональные данные
must-revalidateЗапрещает использовать устаревшую копию без успешной проверкиОтветы, где старая версия недопустима
immutableПоказывает, что ресурс не меняется в срок свежестиФайл с хешем в имени
s-maxageЗадает срок свежести для общих кэшейОтдельная политика CDN

no-cache не запрещает хранение файла. Для запрета сохранения используйте no-store.

Директива s-maxage действует для общих кэшей, а браузер ориентируется на max-age.

Документация MDN о Cache-Control подробно описывает эти директивы.

Как ETag и Last-Modified приводят к ответу 304

Браузер возвращает ETag в заголовке If-None-Match. Для даты изменения он использует If-Modified-Since.

Сервер сравнивает полученный валидатор с текущим ресурсом. Если условие подтверждает отсутствие изменений, сервер отправляет 304 без тела. Браузер использует сохраненный файл.

Сетевой запрос при этом все равно происходит.

Как включить кэширование в .htaccess через mod_expires

Файл .htaccess работает на Apache, если хостинг разрешает такие директивы. Nginx этот файл не читает, поэтому сначала определите сервер, который отдает статику.

Перед правкой скачайте резервную копию .htaccess. Ошибка синтаксиса может вызвать ответ 500 и отключить страницы сайта.

  1. Откройте корневой каталог сайта через файловый менеджер хостинга или SFTP. Включите показ скрытых файлов, если .htaccess не виден.
  2. Попросите хостинг подтвердить работу mod_expires и разрешение директив в .htaccess. При доступе к серверу выполните apachectl -M и найдите expires_module.
  3. Добавьте блок в существующий файл, сохранив правила CMS. Если файла нет, создайте его в корне сайта.
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpeg "access plus 30 days"
    ExpiresByType image/png "access plus 30 days"
    ExpiresByType image/gif "access plus 30 days"
    ExpiresByType image/webp "access plus 30 days"
    ExpiresByType image/avif "access plus 30 days"
    ExpiresByType image/svg+xml "access plus 30 days"
    ExpiresByType image/x-icon "access plus 30 days"
    ExpiresByType text/css "access plus 30 days"
    ExpiresByType text/javascript "access plus 30 days"
    ExpiresByType application/javascript "access plus 30 days"
    ExpiresByType font/woff2 "access plus 30 days"
    ExpiresByType application/pdf "access plus 30 days"
    ExpiresByType text/html "access plus 0 seconds"
</IfModule>

ExpiresActive On включает правила модуля. Каждая строка ExpiresByType сопоставляет срок с MIME-типом ответа.

Для JavaScript предпочтителен text/javascript. Правило для application/javascript охватывает файлы, которые сервер отдает с этим типом.

Блок задает 30 дней, то есть max-age=2592000, для статики.

HTML получает нулевой срок свежести и не хранится месяц без перепроверки. Нулевой срок не запрещает сохранение ответа.

Откройте отдельный CSS-файл после сохранения настроек. В ответе должен появиться Cache-Control с соответствующим сроком и заголовок Expires. Если сервер вернул 500, восстановите резервную копию и изучите журнал ошибок Apache.

Cache-Control через mod_headers

Модуль mod_headers задает политику напрямую. Используйте его вместо конкурирующего правила mod_expires для той же группы файлов.

Применяйте годовой срок только к статике, которая получает новое имя после изменения.

<IfModule mod_headers.c>
    <FilesMatch "\.(css|js|jpe?g|png|gif|webp|avif|svg|ico|woff2)$">
        Header set Cache-Control "max-age=31536000, public, immutable"
    </FilesMatch>
</IfModule>
  • <IfModule mod_headers.c> включает правила при загруженном модуле.
  • <FilesMatch ...> выбирает CSS, JavaScript, изображения, значки и шрифт WOFF2. Последовательность \. означает буквальную точку.
  • Header set Cache-Control ... задает годовой срок, общий кэш и неизменяемость ресурса.
  • </FilesMatch> закрывает выбор файлов.
  • </IfModule> закрывает условие модуля.

Для статики без версии поставьте max-age=86400, public, то есть сутки.

Попросите хостинг включить mod_headers и разрешить директивы категории FileInfo. При доступе к серверу найдите headers_module в выводе apachectl -M. Ответ выбранного файла покажет, сработало ли правило.

Публичному HTML задайте отдельную политику:

<IfModule mod_headers.c>
    <FilesMatch "\.html?$">
        Header set Cache-Control "no-cache"
    </FilesMatch>
</IfModule>

Правило охватывает файлы .htm и .html. CMS часто формирует страницы без расширения, поэтому для таких маршрутов задайте заголовок в приложении.

Как настроить кэширование в nginx

Правила nginx размещают внутри блока server нужного виртуального хоста. Пример рассчитан на каталог /assets/ с файлами, в именах которых есть версия или хеш.

  1. Подключитесь к серверу по SSH и выполните sudo nginx -T. Вывод покажет действующую конфигурацию и подключенные файлы.
  2. Откройте файл с нужным блоком server и сохраните его копию перед изменением.
  3. Добавьте правило внутри server. Если подходящий location уже есть, измените его вместо создания конкурирующего блока.
location ~* ^/assets/.*\.(css|js|jpe?g|png|gif|webp|avif|svg|ico|woff2)$ {
    try_files $uri =404;
    expires off;
    add_header Cache-Control "max-age=31536000, public, immutable";
}

expires off отключает формирование заголовков модулем expires в этом блоке. Полное значение Cache-Control задает одна директива add_header.

Для файлов без версии замените значение на max-age=86400, public.

Если вам достаточно срока свежести без public и immutable, замените обе строки expires и add_header одной:

expires 365d;

Этот вариант задает Expires и Cache-Control с max-age=31536000, то есть на 365 дней.

Добавление add_header внутри location может изменить наследование заголовков родительского блока. Сохраните нужные заголовки безопасности в эффективной конфигурации этого location.

Проверьте конфигурацию командой sudo nginx -t. При успешной проверке выполните sudo nginx -s reload. Запрос к существующему файлу из /assets/ должен вернуть годовой срок и нужные заголовки.

Если проверка конфигурации завершилась ошибкой, не перезагружайте nginx.

Для отката восстановите копию файла, повторите sudo nginx -t и выполните sudo nginx -s reload после успешной проверки.

Какой срок кэширования ставить для разных файлов

Срок выбирайте по способу обновления ресурса. Расширение файла само по себе не показывает, как часто меняется содержимое.

Тип файлаРекомендуемый срокCache-Control
Статика с хешем или версиейГод, 365 днейpublic, max-age=31536000, immutable
Статика без версииСутки, если задержка допустимаpublic, max-age=86400
Общедоступный PDF30 дней, если задержка допустимаpublic, max-age=2592000
Публичный HTMLПерепроверка перед использованиемno-cache
Личная страница без чувствительных данныхХранение только в браузере с перепроверкойprivate, no-cache
Личный кабинетБез сохранения ответаno-store

HTML с no-cache может храниться, но браузер запросит проверку перед повторным использованием. Личные ответы с чувствительными данными отправляйте с no-store, чтобы кэш не сохранил их.

Если файл без версии должен обновляться сразу, используйте no-cache вместо суточного срока.

Как проверить, что кэширование работает

Проверяйте заголовки сервера и повторное использование файла браузером. Один из этих признаков без другого не дает полной картины.

Проверка в DevTools

  1. Откройте сайт в Chrome и запустите инструменты разработчика клавишей F12.
  2. Перейдите на вкладку Network и временно включите «Disable cache». Перезагрузите страницу.
  3. Выберите запрос CSS, картинки или JavaScript. В разделе «Response Headers» найдите Cache-Control, Expires, Content-Type, ETag и Last-Modified.
  4. Снимите «Disable cache» и откройте другую страницу с тем же ресурсом. Сравните объем переданных по сети данных: локальная копия не требует повторной передачи тела файла.

Ответ 304 означает сетевую перепроверку без повторной передачи тела.

Для свежего ресурса браузеру обычно не нужна даже перепроверка. Не включайте принудительную перезагрузку при оценке обычного поведения посетителя: такой режим может обойти кэш.

Проверка заголовков через curl

В терминале выполните команду для существующего файла, подставив адрес своего сайта:

curl -I https://example.com/assets/main.3f9a1c.css

Ключ -I запрашивает заголовки методом HEAD. Для перенаправления добавьте -L и изучите конечный ответ.

Если сервер обрабатывает HEAD иначе, получите заголовки обычного GET-ответа:

curl -sS -D - -o /dev/null https://example.com/assets/main.3f9a1c.css

Вторая команда рассчитана на оболочку с устройством /dev/null, например Linux или macOS. Она выводит заголовки и отбрасывает тело ответа.

Для годовой политики вы увидите Cache-Control: max-age=31536000, public, immutable.

Curl не хранит постоянный кэш браузера, поэтому повторный запуск сам по себе не обязан дать 304. Ответ 200 с ожидаемой политикой хранения подходит для этой проверки.

Проверка в PageSpeed Insights

Запустите аудит публичной страницы в PageSpeed Insights и откройте замечания о сроках хранения статических ресурсов. Сравните перечисленные адреса с заголовками из Network.

Аудит показывает проблему на публичном адресе, но не меняет настройки сервера. Исправляйте те файлы, которые отдает ваш Apache, nginx или CDN.

Как обновить файл, который уже закэширован у посетителей

Измените URL ресурса при публикации новой версии. Браузер воспримет новый адрес как новый файл и не возьмет старую копию.

  1. Опубликуйте измененный файл под новым именем, например main.3f9a1c.css. Его адрес должен вернуть 200 и правильный Content-Type.
  2. Замените ссылку на файл в шаблоне: для CSS измените href, для JavaScript – src.
  3. Откройте страницу и найдите новый адрес в Network. В запросе появится новая версия ресурса.
<link rel="stylesheet" href="/assets/main.3f9a1c.css">
<script src="/assets/main.7bd42e.js" defer></script>

Сборщик проекта может вычислять хеш и обновлять ссылки автоматически. CMS тоже может добавлять версию к имени или URL. Включите этот шаг в процесс публикации, чтобы не менять файлы вручную.

Параметр main.css?v=2 тоже меняет URL.

CDN может исключать параметры из ключа кэша, поэтому хеш в имени обычно предсказуемее для сложной инфраструктуры. При каждом изменении содержимого публикуйте новое имя файла.

Не меняйте версию при каждом открытии страницы. Такой подход отключит повторное использование кэша.

Сохраните предыдущие файлы на сервере на время перехода. Уже открытая страница может продолжать запрашивать старые ресурсы. Для отката верните ссылки на предыдущую версию в шаблоне.

Ошибки при настройке кэширования на сервере

  • После правки .htaccess появился ответ 500. Восстановите копию и проверьте синтаксис, разрешенные директивы и журнал Apache. Директива отсутствующего модуля вне обертки IfModule тоже может вызвать ошибку.
  • Правило внутри IfModule ничего не меняет. Проверьте наличие нужного модуля и разрешения хостинга. Если модуль выключен, Apache пропускает содержимое обертки.
  • HTML хранится месяц. Удалите длинный max-age и назначьте no-cache. Браузеры с уже сохраненным свежим ответом получат новую политику только после следующего обращения к серверу.
  • Личная страница получает public. Отделите персональные маршруты и задайте private или no-store.
  • Файл без версии хранится год. Опубликуйте измененный файл под новым именем и сократите срок для остальных неверсионированных ресурсов.
  • Заголовки не совпадают с настройками. Статику может отдавать другой уровень: nginx, CDN, приложение или хостинг. Измените политику на том уровне, который формирует конечный ответ.
  • В ответе несколько max-age. Уберите конкурирующие правила mod_expires, mod_headers и приложения.
  • После добавления location исчезли заголовки. Проверьте наследование add_header и верните нужные заголовки в эффективный блок.

Совет от нашей редакции: сначала настройте версионирование CSS и JavaScript, затем увеличивайте срок их хранения до года. Так обновления не застрянут у посетителей в старом кэше.

Комментарии

Оставьте свой комментарий
РД
Ризван Д.

Добрый день скажите пожалуйста как сделать кеш для этого https://mc.yandex.ru/metrika/advert.gif (60 минут) а так первый вариант идеально работает а вот с этим чет не работает первый и второй, может что-то не так делаю?

P
Pavel

Расписано отлично, но ничего не работает. Гугл Page speed как спрашивал это сделать, так и спрашивает.

Б
Билл

Спасибо работает, если принимаете заявки то напишите статью

А
Александр

Спасибо, брат! Помог!

СА
Саша А.

Кодировка пишет не подходит(<ifModule mod_expires.c>)
У меня ANSI_X3.4-1968