Если сайт на WordPress грузит одни и те же CSS, JS и изображения на каждом просмотре, проблема часто не в «тяжелой теме», а в том, как сервер отдает заголовки кэша. На практике это видно по повторным запросам к статике, лишним 304/200 в DevTools и жалобам на медленную повторную загрузку страниц у постоянных посетителей.
Задача здесь не в том, чтобы «включить кэш вообще», а в том, чтобы правильно разделить поведение для версионируемых файлов и для тех, что меняются редко. Для WordPress это особенно важно, потому что стили и скрипты часто подключаются через wp_enqueue_style() и wp_enqueue_script(), а изображения лежат в /wp-content/uploads/ и могут кэшироваться слишком агрессивно.
Как понять, что проблема именно в заголовках кэша
Сначала проверьте не абстрактную «скорость», а конкретные признаки. Откройте страницу в Chrome DevTools, вкладку Network, обновите страницу и посмотрите на колонку Size и заголовки ответа. Если CSS и JS каждый раз приходят как 200 OK с полным телом ответа, а не как 304 Not Modified, браузер не может нормально использовать кэш. Если наоборот файлы меняются, но у них слишком длинный max-age, посетитель может видеть старые стили после обновления темы или плагина.
Для быстрой диагностики удобно проверить заголовки через curl:
curl -I https://example.com/wp-content/themes/your-theme/style.cssСмотрите на такие поля:
Cache-Control— сколько и как можно кэшировать;ETag— идентификатор версии ресурса;Last-Modified— дата последнего изменения;Expires— устаревший, но иногда еще встречается;Vary— может ли ответ зависеть от типа клиента или сжатия.
Если у вас уже стоит CDN или плагин кэширования, не спешите править все на сервере. Сначала выясните, кто именно выставляет заголовки: веб-сервер, PHP, плагин или прокси. Иначе можно получить конфликт правил, когда один слой говорит браузеру кэшировать год, а другой — сбрасывать кэш при каждом запросе.
Что настраивать: Cache-Control, ETag или оба сразу
Для WordPress обычно хватает двух сценариев. Первый — для версионируемых файлов, где имя меняется при обновлении: CSS и JS лучше отдавать с длинным сроком жизни, но с версией в URL. Второй — для файлов, которые могут меняться без смены имени, например некоторые изображения или файлы из загрузок, если вы их перезаписываете.
| Подход | Когда подходит | Плюсы | Минусы |
|---|---|---|---|
| Только Cache-Control | Статика с версией в URL | Просто, предсказуемо | Если файл меняется без смены URL, возможен устаревший кэш |
| Cache-Control + ETag | Файлы, которые могут обновляться на том же URL | Браузер может корректно перепроверять ресурс | ETag иногда конфликтует на кластерах и CDN |
| Только ETag | Редко, для узких случаев | Работает как проверка версии | Обычно хуже управляется, чем явный Cache-Control |
Если сайт обслуживается через несколько серверов или CDN, ETag может быть источником проблем: разные узлы генерируют разные значения, и браузер перестает получать стабильный 304. В такой схеме часто разумнее полагаться на Cache-Control и версионирование файлов, а ETag отключить на уровне сервера.
Пошаговая настройка в WordPress
1. Версионируйте CSS и JS в теме и плагинах
Самый надежный способ не ловить «залипший» кэш после обновления — добавлять версию файла в URL через аргумент версии. В WordPress это штатный механизм, и он работает лучше, чем попытки вручную чистить кэш у всех посетителей.
add_action('wp_enqueue_scripts', function () {
$theme = wp_get_theme();
wp_enqueue_style(
'theme-main',
get_stylesheet_directory_uri() . '/assets/css/main.css',
array(),
$theme->get('Version')
);
wp_enqueue_script(
'theme-main',
get_stylesheet_directory_uri() . '/assets/js/main.js',
array('jquery'),
$theme->get('Version'),
true
);
});Такой подход не заменяет серверный кэш, но делает обновления безопаснее: при смене версии темы браузер увидит новый URL и загрузит свежий файл.
2. Задайте Cache-Control для статических файлов на уровне сервера
Если у вас Apache, чаще всего правят .htaccess. Для Nginx логика будет в конфиге виртуального хоста, но принцип тот же: для статики — длинный срок жизни, для HTML — короткий или без агрессивного кэширования.
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|jpg|jpeg|png|gif|webp|svg|woff|woff2)$">
Header set Cache-Control "public, max-age=2592000"
</FilesMatch>
</IfModule>Число в max-age должно соответствовать вашей схеме обновлений. Для CSS и JS месяц часто безопаснее, чем год, если вы не уверены, что всегда меняете версию файла при деплое. Для изображений можно ставить дольше, если они действительно не перезаписываются.
3. Решите, нужен ли ETag
Если сайт один, без сложной балансировки и без CDN, ETag можно оставить. Если у вас несколько серверов, прокси или CDN, проверьте, не ломает ли он кэширование. На Apache ETag иногда отключают, чтобы не было расхождений между узлами.
FileETag NoneЭто не универсальная рекомендация для всех, а рабочая мера, когда вы видите нестабильные 304 или разные ETag на одинаковых файлах в разных ответах. После изменения обязательно сравните заголовки с нескольких запросов и, если есть CDN, с разных точек доступа.
Проверка результата после внедрения
После настройки не ограничивайтесь «страница открывается». Проверьте именно поведение кэша.
- Откройте страницу в инкогнито и посмотрите Network.
- Обновите страницу дважды и сравните, какие ресурсы идут как
200, а какие как304. - Проверьте заголовки через
curl -Iдо и после очистки кэша. - Измените один CSS-файл и убедитесь, что браузер получает новый URL или новую версию ответа.
- Если есть CDN, проверьте не только origin, но и ответ через CDN-домен.
Хороший признак — HTML не кэшируется слишком агрессивно, а статика отдается с понятным сроком жизни. Плохой признак — после обновления темы стили не меняются до ручной очистки кэша у пользователя. Это почти всегда значит, что вы кэшируете файл без версионирования или CDN игнорирует обновление.
Частые ошибки и как их исправить
Слишком длинный Cache-Control для файлов без версии
Если вы ставите max-age=31536000 на CSS, но не меняете URL при обновлении, старые стили будут жить у пользователей очень долго. Исправление простое: либо добавляйте версию в URL, либо уменьшайте срок кэширования.
ETag конфликтует с CDN или несколькими серверами
На одном сервере ETag может работать нормально, а на кластере — давать нестабильные ответы. Если видите, что браузер не получает ожидаемые 304, временно отключите ETag и проверьте поведение снова.
Кэшируется HTML вместо статики
Иногда правило написано слишком широко и затрагивает не только CSS/JS, но и страницы сайта. Это приводит к устаревшему контенту, проблемам с авторизацией и странному поведению форм. Ограничивайте правила расширениями файлов или отдельными путями.
Плагин кэша и серверные заголовки спорят друг с другом
Если плагин уже ставит свои заголовки, а вы добавляете еще и серверные правила, итог может быть непредсказуемым. Сначала посмотрите фактический ответ сервера, потом решайте, где держать единственный источник правды: в плагине, в конфиге веб-сервера или в CDN.
Практические советы по безопасности и производительности
Не кэшируйте вслепую все подряд. Для админки, страницы входа и пользовательских сессий кэш должен быть аккуратным или отсутствовать. Для публичной статики — наоборот, кэш нужен, но только если вы контролируете обновление файлов.
Если вы часто правите тему вручную, заведите привычку увеличивать версию ассетов при каждом деплое. Это можно делать через версию темы, хэш сборки или параметр в wp_enqueue_style(). Главное — не оставлять один и тот же URL для меняющегося файла.
Если на сайте много технических проблем с дублями, мусорными скриптами и лишними запросами, иногда проще сначала привести базовую оптимизацию в порядок, а уже потом тонко настраивать заголовки. В таких случаях полезно использовать инструменты, которые помогают чистить лишнее и управлять SEO-настройками на уровне сайта, например Clearfy Pro: https://wpshop.ru/plugins/clearfy.
Но даже с плагином не стоит отключать проверку руками. После любого изменения заголовков прогоняйте реальный сценарий: первая загрузка, повторная загрузка, обновление файла, очистка кэша CDN, проверка на мобильном и десктопе. Именно так видно, что настройка не просто «включилась», а действительно работает.