Если сайт на WordPress уже использует кеш страниц, но Lighthouse или DevTools все равно ругаются на медленную загрузку ресурсов, часто проблема не в HTML, а в заголовках для статических файлов. Браузер может каждый раз перепроверять CSS, JS, шрифты и изображения, хотя эти файлы меняются редко. В итоге лишние запросы остаются, а повторный визит не ускоряется так, как мог бы.
Здесь важно не путать кеш страниц и кеш статических файлов. Первый отвечает за HTML, второй — за то, как браузер хранит ассеты у пользователя. Для WordPress это особенно заметно на темах и плагинах, где CSS и JS подключаются с версией в URL, но сервер отдает слишком короткий или вообще отсутствующий Cache-Control.
Когда проблема действительно в Cache-Control
Сначала стоит проверить, что именно происходит в ответах сервера. Если в DevTools на вкладке Network у CSS или JS вы видите 200 OK при каждом обновлении страницы вместо 304 Not Modified или загрузки из disk cache, заголовки настроены неудачно. Еще один признак — в Lighthouse есть замечание про эффективную политику кеширования для статических ресурсов.
Что смотреть в первую очередь
- есть ли у CSS, JS, шрифтов и изображений заголовок
Cache-Control; - не слишком ли маленький
max-age; - не отключает ли сервер кеширование через
no-cacheилиno-store; - не меняется ли URL файла при каждом запросе из-за неправильной версии в
wp_enqueue_style()илиwp_enqueue_script(); - не отдает ли CDN или прокси свои заголовки поверх настроек сервера.
Если файлы уже версионируются через ?ver=, это помогает при обновлениях, но не заменяет нормальный Cache-Control. Браузер все равно должен понимать, что ресурс можно хранить локально достаточно долго.
Какой вариант настройки выбрать
В WordPress есть три рабочих пути: правка конфигурации веб-сервера, настройка через панель хостинга или подключение плагина, который умеет управлять заголовками. Самый надежный вариант — серверный, потому что он не зависит от темы и не ломается после обновления плагинов.
| Подход | Плюсы | Минусы | Когда брать |
|---|---|---|---|
| Конфиг сервера | Стабильно, быстро, без лишнего кода | Нужен доступ к nginx/apache | Есть доступ к конфигам или поддержка хостинга |
| Плагин для заголовков | Можно настроить без сервера | Риск конфликтов и лишней логики | Нет доступа к серверу, нужен быстрый фикс |
| Код в теме/му-плагине | Гибко для отдельных типов файлов | Нужно аккуратно тестировать | Нужна точечная логика для конкретных ресурсов |
Пошаговая настройка через сервер
Если сайт работает на nginx, обычно достаточно добавить правила для статических файлов. Ниже пример, который можно адаптировать под свой конфиг. Он задает долгий кеш для ассетов, которые редко меняются, и не трогает HTML.
location ~* \.(?:css|js|mjs|jpg|jpeg|gif|png|svg|webp|avif|ico|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable" always;
access_log off;
}
Здесь есть важная деталь: immutable имеет смысл только если URL файла меняется при обновлении. Для CSS и JS в WordPress это обычно решается версией в URL или сменой имени файла при деплое. Если вы обновляете файл, но URL остается прежним, слишком агрессивный кеш приведет к тому, что пользователь увидит старую версию.
Для Apache логика похожая, но реализуется через mod_expires и mod_headers. Пример для .htaccess:
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 30 days"
ExpiresByType application/javascript "access plus 30 days"
ExpiresByType image/jpeg "access plus 30 days"
ExpiresByType image/png "access plus 30 days"
ExpiresByType image/webp "access plus 30 days"
ExpiresByType font/woff2 "access plus 30 days"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|jpg|jpeg|png|gif|svg|webp|avif|woff|woff2)$">
Header set Cache-Control "public, max-age=2592000"
</FilesMatch>
</IfModule>
Если доступ к серверу ограничен: настройка через код
Когда хостинг не дает править nginx или Apache, можно добавить заголовки на уровне WordPress. Это не лучший вариант для всех ресурсов, но для части ассетов он работает. Удобнее всего вынести код в mu-plugin, чтобы он не зависел от темы.
<?php
/**
* Plugin Name: Static Cache Headers
*/
add_filter('wp_headers', function ($headers) {
if (is_admin()) {
return $headers;
}
$headers['Cache-Control'] = 'public, max-age=3600';
return $headers;
});
Но этот фильтр влияет на HTML-ответы WordPress, а не на файлы в /wp-content/uploads/ или ресурсы, которые отдает сам веб-сервер. Поэтому использовать его как единственное решение не стоит. Он полезен только как временная мера или для отдельных сценариев, где нужно быстро проверить гипотезу.
Если задача именно в заголовках для медиафайлов, лучше настраивать сервер или CDN. Для WordPress-кода разумнее управлять версией подключаемых файлов, чтобы браузер понимал, когда ресурс обновился.
Как правильно версионировать CSS и JS в теме
Частая ошибка — подключить файл без версии или с одинаковой версией навсегда. Тогда браузер может держать старый CSS после деплоя. В WordPress правильнее использовать версию темы или время изменения файла в разработке.
<?php
wp_enqueue_style(
'theme-main',
get_stylesheet_directory_uri() . '/assets/css/main.css',
array(),
filemtime(get_stylesheet_directory() . '/assets/css/main.css')
);
wp_enqueue_script(
'theme-main',
get_stylesheet_directory_uri() . '/assets/js/main.js',
array('jquery'),
filemtime(get_stylesheet_directory() . '/assets/js/main.js'),
true
);
Такой подход удобен на этапе разработки и для небольших сайтов. На продакшене у него есть компромисс: filemtime() каждый раз обращается к файловой системе. Обычно это не критично, но если у вас много ассетов и высокая нагрузка, лучше использовать версию сборки или манифест из пайплайна деплоя.
Проверка результата после внедрения
После настройки не ограничивайтесь общим ощущением, что сайт стал быстрее. Проверьте конкретные ответы сервера.
- Откройте страницу в Chrome DevTools.
- Перейдите на вкладку Network.
- Обновите страницу с отключенным кешем и затем еще раз без жесткой перезагрузки.
- Проверьте заголовки у CSS, JS, шрифтов и изображений.
- Сравните поведение до и после: должны появиться
from disk cache,memory cacheили304 Not Modifiedтам, где это уместно.
Дополнительно можно проверить заголовки через curl:
curl -I https://example.com/wp-content/themes/your-theme/assets/css/main.css
В ответе ищите Cache-Control, Expires и при необходимости ETag. Если заголовок есть, но браузер все равно не кеширует файл, проверьте, не перезаписывает ли его CDN или серверная панель хостинга.
Частые ошибки и как их исправить
Слишком короткий max-age
Если поставить кеш на 5 минут или 1 час для файлов, которые меняются раз в несколько недель, вы просто не используете потенциал браузерного кеша. Для статических ассетов обычно нужен более длинный срок, но только при нормальной версионизации файлов.
Одинаковый URL у обновленного CSS
Это классическая причина «почему стили не обновились после правки». Если файл меняется, а URL нет, браузер продолжает отдавать старую версию. Решение — версия в wp_enqueue_style(), смена имени файла или сброс кеша на CDN.
Кеширование HTML как статического файла
Нельзя бездумно ставить долгий кеш на HTML-страницы через те же правила, что и для изображений. Для страниц WordPress нужны отдельные механизмы: page cache, reverse proxy или кеш плагина. Иначе можно получить устаревшие формы, меню, цены или персональные блоки.
Конфликт с CDN
Если CDN уже выставляет свои заголовки, локальная настройка может не сработать или даст неожиданный результат. В таком случае сначала смотрите ответ именно конечного URL через curl -I и проверяйте, кто отдает файл: origin-сервер или CDN.
Чек-лист перед выкладкой на продакшен
- CSS и JS подключаются с версией, которая меняется при обновлении файла.
- Для изображений и шрифтов задан разумный срок кеша.
- HTML не получает агрессивный
Cache-Controlиз правил для ассетов. - CDN не перезаписывает заголовки неожиданным образом.
- После обновления темы или плагина старые файлы не остаются в кеше пользователя слишком долго.
- Проверка в DevTools показывает ожидаемое поведение: cache hit, 304 или корректный новый ответ после смены версии.
Практика безопасности и производительности
Не стоит ставить бесконечный кеш на все подряд. Для изображений и шрифтов это часто допустимо, если URL меняется при обновлении. Для JS и CSS — тоже, но только при дисциплине деплоя. Если у вас ручные правки на продакшене без смены версии, лучше выбрать более консервативный срок и не полагаться на память браузера.
Если нужен более широкий набор технических правок для WordPress — от чистки дублей до управления индексируемостью и служебными настройками — иногда удобнее собрать это в одном инструменте, например через Clearfy Pro: https://wpshop.ru/plugins/clearfy?utm_source=wponline.ru&utm_medium=article&utm_campaign=nastrojka-cache-control-dlya-staticheskih-faylov-v-wordpress. Но даже в этом случае заголовки кеша для статических файлов лучше проверять отдельно, потому что итог всегда зависит от сервера и CDN.
Когда настройка сделана правильно, повторные визиты становятся дешевле для браузера, а обновления файлов не ломают внешний вид сайта. Самое важное здесь — не смешивать кеш HTML и кеш ассетов и не забывать проверять заголовки после каждого изменения конфигурации.