Кэширование сайта: четыре уровня и какой включать первым
Браузерный кэш, кэш страниц, объектный кэш и OPcache. Что каждый даёт по цифрам, в каком порядке включать и где ловят устаревшие данные.
Содержание
- Четыре уровня и что каждый ускоряет
- OPcache: включён ли он у вас вообще
- Браузерный кэш: экономия на повторных визитах
- Кэш страниц: где лежат основные секунды
- Объектный кэш: нужен ли сайту на 50 страниц
- Порядок включения на живом сайте
- Грабли, на которых теряют часы
- Что и когда сбрасывать
- С чего начать на своём сайте
- Частые вопросы
Кэш бывает четырёх уровней: OPcache для байт-кода PHP, объектный для запросов к базе, кэш готовых страниц и браузерный. Начинают с OPcache: он снимает 30-60 процентов времени PHP и на живом сайте почти ничего не ломает.
Кэширование сайта обычно включают как таблетку от всего: поставили плагин, увидели в PageSpeed плюс двенадцать баллов, закрыли вкладку. Через неделю владелец правит цену на странице услуги, жмёт обновить, а там старая цифра. Или в магазине корзина показывает чужие товары. Разберу четыре уровня кэша, которые есть у любого сайта на PHP, что каждый реально ускоряет в цифрах, в каком порядке их стоит включать и почему добрая половина жалоб «правки не приезжают» приходится не на тот уровень, на который все думают.

Пишу по абонентке: за шесть лет я заходил на чужие сайты примерно с одним и тем же набором находок. Плагин кэширования стоит и настроен как попало, OPcache выключен, а объектный кэш кто-то поставил «для скорости» на сайте из сорока страниц, где он не нужен вообще.
Четыре уровня и что каждый ускоряет
Слово «кэш» в разговоре с подрядчиком означает четыре разные вещи. Они живут в разных местах, включаются по-разному и ломаются тоже по-разному.
| Уровень | Что хранит | Кого ускоряет | Типовой выигрыш |
|---|---|---|---|
| OPcache | скомпилированный байт-код PHP | генерацию любой страницы | 30-60 процентов времени PHP |
| Объектный кэш | результаты запросов к базе | тяжёлые страницы и админку | 10-40 процентов, зависит от базы |
| Кэш страниц | готовый HTML целиком | анонимных посетителей | TTFB с 600 мс до 40-80 мс |
| Браузерный кэш | статику на стороне клиента | повторные визиты и переходы внутри сайта | вторая страница почти без сетевых загрузок |
Порядок в таблице идёт снизу вверх по стеку, но включать я советую в другом. Сначала OPcache: он бесплатный, ставится один раз и не создаёт устаревших данных на фронте. Потом браузерный кэш заголовками, потому что это правка в конфиге и она ничего не ломает. Потом кэш страниц, самый мощный и самый капризный. Объектный кэш последним, и то не всем.
Ещё есть уровни, которые я тут не считаю отдельно: кэш CDN и кэш DNS. Первый по сути тот же кэш страниц, только вынесенный на чужие сервера. Второй к скорости отрисовки отношения почти не имеет.
OPcache: включён ли он у вас вообще
Каждый раз, когда PHP выполняет файл, он его читает, парсит и компилирует в байт-код. На типовом WordPress с двадцатью плагинами на один запрос подтягивается 300-900 файлов. OPcache компилирует один раз и держит результат в памяти.
Проверить можно из консоли, если есть SSH:
php -i | grep -E 'opcache.enable|opcache.memory|validate_timestamps'
# opcache.enable => On => On
# opcache.memory_consumption => 128 => 128
# opcache.validate_timestamps => On => On
Если SSH нет, кладём в корень временный файл и открываем в браузере:
<?php
$s = opcache_get_status();
echo $s === false ? "OPcache выключенn" : sprintf(
"занято %d МБ, свободно %d МБ, файлов %d, промахов %dn",
$s['memory_usage']['used_memory'] >> 20,
$s['memory_usage']['free_memory'] >> 20,
$s['opcache_statistics']['num_cached_scripts'],
$s['opcache_statistics']['misses']
);
Файл после проверки удалить, а не оставлять на боевом сайте с говорящим именем вроде info.php. Его находят сканеры за сутки.
На что смотреть в выводе. memory_consumption меньше 128 МБ для WordPress с плагинами маловато, кэш будет переполняться и сбрасываться целиком. max_accelerated_files по умолчанию 10 000, для Битрикса это мало, там счёт файлов идёт на десятки тысяч. Если num_cached_scripts упирается в лимит, а misses растут, кэш работает вхолостую.
Замер с одного клиентского сайта, WordPress с WooCommerce на shared-хостинге Timeweb: время генерации главной без OPcache 780 мс, с OPcache 310 мс. Ничего не меняли, кроме одной строки в конфиге PHP. Это самое дешёвое ускорение из всех, что вообще бывает, и оно до сих пор выключено примерно на каждом пятом сайте, который я принимаю на обслуживание.
Отдельная тонкость: validate_timestamps. Если он выключен (так делают на серьёзных проектах ради лишних процентов), PHP перестаёт проверять дату изменения файлов. Вы залили новую версию темы, а сайт отдаёт старый код, пока не перезапустят пул php-fpm. На shared-хостинге этот параметр обычно включён, и слава богу.
Браузерный кэш: экономия на повторных визитах
Тут кэш живёт у посетителя. Сервер в заголовке ответа говорит: этот файл можно не спрашивать у меня следующие столько-то секунд. Правильно настроенный браузерный кэш не влияет на первый визит вообще, зато вторая и третья страница открываются почти мгновенно.
Минимальный рабочий блок для Apache:
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "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 text/html "access plus 0 seconds"
</IfModule>
<FilesMatch ".(css|js|woff2|webp|avif)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
Год на статику пугает владельцев сайтов, и зря. Ключ к безопасности этой схемы не в сроке, а в версионировании: адрес файла должен меняться при изменении содержимого. WordPress делает это сам через ?ver=, но по умолчанию подставляет версию темы, а не дату файла. То есть вы поправили style.css, версия темы прежняя, у половины посетителей неделю висит старая вёрстка.
Лечится одной строкой в functions.php:
wp_enqueue_style(
'saitfix-main',
get_stylesheet_directory_uri() . '/assets/css/main.css',
[],
filemtime(get_stylesheet_directory() . '/assets/css/main.css')
);
Теперь версия равна времени последнего изменения файла. Правка приехала мгновенно, кэш остальных файлов не сброшен. Я на этом сам однажды потерял час, отлаживая «несуществующий баг» в стилях, которые давно были правильными. Подробнее про заголовки, сроки и типовые ошибки в конфиге писал отдельно: кэширование через htaccess.
HTML кэшировать в браузере нельзя. Ровно ноль секунд, иначе посетитель будет неделю видеть снятую с продажи акцию, и никакой сброс на сервере ему не поможет.
Кэш страниц: где лежат основные секунды
Самый заметный уровень. Сервер один раз собирает страницу, складывает готовый HTML и дальше отдаёт его файлом, вообще не запуская PHP и не трогая базу. TTFB падает в разы: типичные 500-700 мс на shared превращаются в 40-80 мс.
Плагин или кэш на сервере
На shared-хостинге выбор обычно между плагином CMS и кэшем веб-сервера.
Если хостинг на LiteSpeed, ставьте LiteSpeed Cache и не думайте. Он бесплатный, кэш живёт на уровне сервера, а не в PHP, и по скорости уделывает всё остальное. Разница с плагинами, которые хранят кэш файлами и всё равно поднимают PHP на каждый запрос, заметна на глаз.
Если Apache или nginx без LiteSpeed, вариантов два. WP Super Cache и Cache Enabler для WordPress отдают файловый кэш и на среднем сайте услуг дают ровно то, что нужно. WP Rocket стоит порядка 5 500 рублей в год за один сайт, из полезного там в основном удобный интерфейс и отложенная загрузка скриптов, а сам кэш не быстрее бесплатных.
У Битрикса кэш свой и многослойный: кэш компонентов, автокэш, HTML-кэш страниц. Отдельный плагин ему не нужен, нужен человек, который включит автокомпозит и не забудет про кэш меню.
У Тильды кэш на их стороне, крутить нечего. Это одновременно плюс (ничего не сломаешь) и минус (ничего не ускоришь).
Что нельзя пускать в кэш страниц
Вот тут ломается всё интересное. Кэш страниц отдаёт один и тот же HTML всем подряд, поэтому из него надо исключить:
- корзину, оформление заказа, личный кабинет, страницу «мои заказы»,
- любые страницы для залогиненных пользователей (обычно плагин делает это сам, проверьте),
- страницы благодарности с уникальным номером заявки,
- админку и всё, что начинается с
/wp-admin/, - ответы AJAX и REST API, они не HTML и кэшируются отдельными правилами,
- страницы с параметрами UTM, если счётчик читает их из URL на сервере.
Проверять надо руками, в режиме инкогнито, после каждой перенастройки. Открыли товар, положили в корзину, обновили страницу другого товара, посмотрели, не показывает ли корзина чужое.
Объектный кэш: нужен ли сайту на 50 страниц
Объектный кэш держит в памяти результаты запросов к базе: настройки, меню, метаданные, результаты сложных выборок. В WordPress он есть всегда, но по умолчанию живёт ровно один запрос и умирает. Redis или Memcached делают его постоянным между запросами.
Честный ответ на вопрос из заголовка: сайту услуг на 50 страниц объектный кэш не нужен. Он даёт выигрыш там, где база отвечает медленно и запросов много: магазин с фильтрами, каталог на несколько тысяч позиций, сайт с активной админкой и десятком редакторов.
Ставить Redis на визитку смысла нет по трём причинам. На shared его чаще всего просто нет. Он добавляет ещё один слой, который может отдавать устаревшие данные. И выигрыш в 30-40 мс на фоне кэша страниц, который снимает 500 мс, теряется в шуме.
Зато перед тем как думать про Redis, стоит посмотреть, не разбухла ли автозагрузка опций: на запущенных сайтах она вырастает до нескольких мегабайт и тормозит каждый запрос, включая админку. Разбор с командами: раздутая база WordPress и autoload. Часто там лежит вся проблема, и объектный кэш просто маскирует её.
Проверить, включён ли объектный кэш в WordPress, можно наличием файла wp-content/object-cache.php. Если файла нет, кэша нет, что бы ни было написано в настройках плагина.
Порядок включения на живом сайте
Чек-лист, по которому я захожу на новый проект. Каждый шаг с замером до и после, иначе спорить придётся на ощущениях.
- Замерить исходное состояние: TTFB через
curl, время генерации страницы, PageSpeed для мобильных. Записать цифры. - Проверить OPcache. Включить, поднять
memory_consumptionдо 192 МБ,max_accelerated_filesдо 20 000 на Битриксе. Замерить. - Настроить заголовки браузерного кэша и версионирование по
filemtime. Проверить, что правка CSS приезжает сразу. - Включить кэш страниц. Исключить корзину, кабинет, страницы благодарности. Пройти сценарий заявки и заказа руками.
- Отдельно проверить формы: заявка уходит, письмо приходит, цель в Метрике фиксируется.
- Замерить снова и сравнить с пунктом 1. Если выигрыша нет, кэш не работает, а не «хостинг такой».
- Только теперь смотреть на объектный кэш, и только если база реально узкое место.
Замер TTFB одной строкой:
curl -o /dev/null -s -w "dns %{time_namelookup}s connect %{time_connect}s ttfb %{time_starttransfer}s total %{time_total}sn" https://example.ru/
Прогоните пять раз подряд. Первый ответ будет медленным (кэш холодный), остальные покажут реальную картину. Если после включения всех уровней TTFB так и остался за 600 мс, дело не в кэше: разбор причин собран в статье про TTFB больше 600 мс на shared-хостинге.
Грабли, на которых теряют часы
Правки не приезжают. Классика жанра. Порядок проверки строго такой: сначала браузер в инкогнито, потом сброс кэша страниц, потом сброс OPcache, потом кэш CDN, если он есть. В девяти случаях из десяти виноват браузерный кэш и отсутствие версионирования, а искать почему-то начинают с плагина.
Корзина показывает чужие товары. Кэш страниц закэшировал страницу вместе с содержимым корзины и отдаёт её всем. Лечится исключением нужных страниц и куки из кэша. Симптом пугающий, похож на утечку данных, но это обычно просто кривая настройка.
Форма шлёт старый токен. Nonce в WordPress живёт 12-24 часа. Если страница с формой закэширована на неделю, посетитель получает протухший токен и отправка молча падает. Лечение: исключить страницу с формой из кэша либо подгружать nonce отдельным запросом. Заметить это без мониторинга заявок невозможно, форма просто перестаёт работать, а владелец думает, что клиенты не пишут.
Счётчик считает не то. Если кэш отдаёт HTML с зашитым идентификатором сессии или страницу благодарности всем подряд, статистика поплывёт. Проверять цели после каждой перенастройки кэша.
Два плагина кэширования сразу. Видел трижды. Один пишет файлы, второй их же переписывает, сайт отдаёт мешанину и периодически 500-ю ошибку. Плагин кэширования на сайте должен быть один. Если не уверены, какой из установленных грузит сайт, а какой ускоряет, поможет разбор про то, какой плагин тормозит сайт.
Что и когда сбрасывать
| Что изменили | Что сбросить |
|---|---|
| Текст или картинку на странице | кэш этой страницы (обычно сбрасывается сам при сохранении) |
| Меню, шапку, футер | кэш страниц целиком |
| CSS или JS темы | ничего, если работает версионирование по filemtime |
| PHP-файлы темы или плагина | OPcache, затем кэш страниц |
| Цены, остатки, каталог | кэш страниц и объектный кэш |
| Настройки CMS и плагинов | всё подряд, включая объектный кэш |
Сброс OPcache через WP-CLI или консоль:
wp cache flush # объектный кэш WordPress
wp litespeed-purge all # кэш страниц LiteSpeed
php -r 'opcache_reset();' # НЕ сработает для веб-процесса
Последняя строка популярная ловушка: php -r запускает отдельный процесс CLI со своим кэшем, а веб-сайт крутится в php-fpm и живёт своим. Сбрасывать OPcache для сайта надо либо запросом через веб (тот же временный скрипт с opcache_reset()), либо перезапуском пула php-fpm из панели хостинга.
Отдельно про автоматику: настройте сброс кэша страниц по событию публикации и правки записи, а не по расписанию раз в сутки. Раз в сутки означает, что владелец правит телефон в шапке утром, а видит его вечером, и весь день звонит вам с вопросом, почему ничего не работает.
С чего начать на своём сайте
Откройте консоль хостинга и проверьте OPcache. Это пять минут и половина эффекта. Дальше поставьте один плагин кэша страниц, исключите корзину и формы, прогоните сценарий заявки руками. Заголовки браузерного кэша добавьте в .htaccess вместе с версионированием ассетов, иначе годовой кэш превратится в проблему на первой же правке.
Если сайт после этого всё равно отвечает по секунде, кэш не виноват и дальше крутить его бесполезно: смотрите тариф хостинга, тяжёлые плагины и запросы к базе.
Настройка всех четырёх уровней с замерами до и после, проверкой форм и корзины входит в абонентское обслуживание, отдельной строкой я это не считаю. Тарифы с составом работ лежат на странице цен, а если сайт тормозит прямо сейчас и надо разобраться сегодня, напишите через форму на главной. Другие разборы по скорости собраны в рубрике про скорость.
Частые вопросы
С какого кэша начинать
С OPcache. Он кэширует скомпилированный код PHP, ускоряет генерацию любой страницы на 30-60 процентов и не зависит от логики сайта. На типовом хостинге время генерации главной падает почти вдвое.
Нужен ли объектный кэш маленькому сайту
Сайту на полсотни страниц обычно нет. Он даёт выигрыш там, где база большая и запросы тяжёлые: магазины, каталоги, сайты с фильтрами.
Что даёт кэш страниц
Самое заметное ускорение для анонимных посетителей: время до первого байта падает с сотен миллисекунд до нескольких десятков. Но именно он и вызывает жалобы на то, что правки не видны.
Почему после правки видно старую цену
Сработал кэш страниц: посетителю отдали сохранённый HTML. Правильно настроенный кэш сбрасывается при сохранении записи автоматически, а если этого не происходит, настройку надо чинить, а не отключать кэш целиком.