Если сайт на WordPress уже использует серверный или плагинный кэш, это не отменяет отдельную проблему: браузер может слишком часто заново запрашивать CSS, JS, шрифты и изображения. В результате страница вроде бы отдается быстро, но в DevTools видно лишние 200/304-ответы, а при каждом обновлении темы или плагина пользователи дольше видят старые стили.
Ниже разберем, как настроить браузерный кэш для статических файлов так, чтобы не сломать обновления ассетов и не упереться в конфликт с плагином кэширования.
Когда браузерный кэш реально нужен
Эта настройка полезна, если у вас:
- много CSS и JS-файлов, которые редко меняются;
- тяжелые изображения и шрифты, которые повторно загружаются при каждом визите;
- плагин кэширования уже стоит, но Lighthouse или WebPageTest все равно ругаются на отсутствие cache headers;
- после обновления темы пользователи видят старые стили дольше, чем ожидается.
Важно не путать браузерный кэш с серверным. Серверный кэш хранит готовую HTML-страницу, а браузерный кэш управляет тем, как клиент хранит статические файлы локально.
Диагностика: что проверить до изменений
Сначала посмотрите, какие заголовки реально отдает сервер. Это можно сделать в DevTools браузера или через curl.
curl -I https://example.com/wp-content/themes/your-theme/style.cssВ ответе ищите:
Cache-Control;Expires;ETag;Last-Modified.
Если заголовков нет или они слишком короткие, браузер будет чаще обращаться к серверу. Если заголовки есть, но файл все равно запрашивается заново, проверьте, не добавляет ли плагин кэширования собственные правила, которые конфликтуют с настройками сервера.
Что считать нормальным поведением
Для статических файлов обычно нормально видеть долгий срок жизни кэша и версионирование файлов через параметр ?ver= или через имя файла. Тогда браузер держит старую копию, а при обновлении WordPress или темы получает новый URL и скачивает свежую версию.
Пошаговая настройка через .htaccess
Если сайт работает на Apache, самый прямой способ — задать заголовки для статических файлов в .htaccess. Это не требует отдельного плагина и подходит для большинства типовых установок.
<IfModule mod_expires.c>
ExpiresActive On
# Изображения
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
# Шрифты
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
ExpiresByType application/x-font-ttf "access plus 1 year"
ExpiresByType application/x-font-opentype "access plus 1 year"
# CSS и JS
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|jpg|jpeg|png|gif|webp|svg|woff|woff2|ttf|eot)$">
Header set Cache-Control "public, max-age=2592000"
</FilesMatch>
</IfModule>Здесь есть важный нюанс: для изображений можно ставить более длинный срок, чем для CSS и JS. Стили и скрипты чаще меняются, и слишком агрессивный срок жизни без версионирования приведет к тому, что пользователи будут видеть старую верстку.
Если сайт на Nginx
На Nginx правила задаются в конфигурации сервера. Это удобнее, чем пытаться чинить кэш на уровне WordPress, потому что браузерный кэш должен отдаваться как можно раньше.
location ~* \.(css|js|jpg|jpeg|png|gif|webp|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
access_log off;
}
location ~* \.(html)$ {
add_header Cache-Control "no-cache, must-revalidate";
}Не копируйте этот блок бездумно в общий location /. Нужен именно отдельный матчинг для статических файлов. Иначе можно случайно повлиять на HTML-страницы, а это уже другой уровень кэширования.
Как не сломать обновление CSS и JS
Главная ошибка — поставить длинный срок жизни кэша и забыть про версионирование. Если файл style.css обновился, а URL остался тем же, браузер может продолжать использовать старую копию до истечения срока.
В WordPress правильнее всего подключать ассеты через wp_enqueue_style() и wp_enqueue_script() с версией, завязанной на номер файла или время сборки.
function mytheme_assets() {
wp_enqueue_style(
'mytheme-main',
get_stylesheet_directory_uri() . '/assets/css/main.css',
array(),
filemtime(get_stylesheet_directory() . '/assets/css/main.css')
);
wp_enqueue_script(
'mytheme-main',
get_stylesheet_directory_uri() . '/assets/js/main.js',
array('jquery'),
filemtime(get_stylesheet_directory() . '/assets/js/main.js'),
true
);
}
add_action('wp_enqueue_scripts', 'mytheme_assets');filemtime() здесь полезен тем, что меняет версию только при изменении файла. Это практичнее, чем вручную править строку версии после каждого деплоя.
Сравнение подходов: плагин, сервер, код
| Подход | Когда подходит | Минус |
|---|---|---|
| Плагин кэширования | Если нужен быстрый старт без доступа к серверу | Не всегда управляет заголовками так точно, как сервер |
| .htaccess / Nginx | Если есть доступ к конфигу и нужен контроль над заголовками | Требует аккуратности и проверки после правок |
| Версионирование в коде темы | Если проблема в старых CSS/JS после обновлений | Не заменяет сами cache headers |
Проверка результата после внедрения
После правок не ограничивайтесь визуальной проверкой сайта. Нужно убедиться, что заголовки действительно изменились.
- Откройте страницу в режиме инкогнито.
- В DevTools перейдите на вкладку Network.
- Обновите страницу и кликните по CSS или JS-файлу.
- Проверьте заголовки ответа:
Cache-Control,Expires,Age.
Если все настроено правильно, при повторной загрузке часть статических файлов должна уходить со статусом 304 Not Modified или вообще браться из disk cache браузера. Для файлов с длинным сроком жизни это нормальный сценарий.
Дополнительно можно проверить через curl -I и сравнить ответ до и после изменения конфигурации.
Частые ошибки и как их исправить
Слишком длинный кэш без версионирования
Если вы ставите срок на год, но не меняете URL файлов, пользователи будут видеть старые стили после редактирования темы. Решение простое: добавьте версию через filemtime() или через сборку ассетов.
Конфликт с плагином кэширования
Некоторые плагины уже добавляют свои заголовки или переписывают правила на уровне сервера. Если после настройки ничего не меняется, временно отключите плагин и проверьте заголовки снова. Так проще понять, кто именно перезаписывает ответ.
Правка не в том месте
На Apache часто редактируют не тот .htaccess, а на Nginx пытаются решить задачу через WordPress-хуки. Для браузерного кэша это не лучший путь: заголовки должны отдаваться на уровне веб-сервера, а не PHP.
Кэш для HTML включен случайно
Если в правила попал text/html с длинным сроком жизни, можно получить устаревшие страницы и проблемы с авторизацией. HTML лучше не смешивать с кэшированием ассетов без отдельной логики.
Что еще стоит проверить для производительности
Если вы уже настраиваете кэш заголовки, имеет смысл посмотреть и на сами ассеты. Часто проблема не в отсутствии cache headers, а в том, что тема грузит слишком много файлов.
- уберите неиспользуемые библиотеки;
- объединяйте только те CSS/JS, где это действительно оправдано;
- проверьте, не тянутся ли шрифты с лишними начертаниями;
- не оставляйте старые файлы в
/uploadsи/assets, если они больше не используются.
Если нужен отдельный инструмент для чистки дублей, лишних мета-тегов и технического мусора на сайте, иногда удобнее смотреть в сторону решений вроде Clearfy Pro: https://wpshop.ru/plugins/clearfy. Но саму задачу браузерного кэша он не заменяет — это именно вспомогательный слой.
После внедрения сохраните себе короткий чек-лист:
- проверить заголовки через
curl -I; - убедиться, что CSS и JS версионируются;
- посмотреть Network в DevTools;
- протестировать обновление темы или плагина после очистки кэша;
- сравнить поведение в обычном окне и в инкогнито.
Если эти проверки проходят, значит браузерный кэш настроен не формально, а рабочим образом: файлы не перегружаются без причины, а обновления не застревают в старой версии.