WordPress тормозит редко из-за самого ядра. Обычно время ответа съедают десятки плагинов, каждый из которых грузит свои скрипты, стили и запросы к базе. Лечить это установкой ещё пяти «ускоряющих» плагинов — путь в противоположную сторону. Кэширование лучше строить на уровне сервера: OPcache для PHP, страничный кэш в nginx, один объектный кэш и аккуратная работа с базой. Ниже — схема без плагинов-бомб, которую можно проверить на любом LEMP-сервере.
Сначала измерьте, потом ускоряйте
Без замеров легко оптимизировать не то. Отделите сеть и TLS от времени генерации страницы.
curl -s -o /dev/null -w "dns=%{time_namelookup} connect=%{time_connect} ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.ru/
wp db size --path=/var/www/example.ru --tables
wp plugin list --status=active --path=/var/www/example.ru
wp core version --path=/var/www/example.ru
Если ttfb превышает 600–800 мс, узкое место почти наверняка в PHP или базе, а не в канале. Список активных плагинов покажет, сколько кода выполняется на каждом запросе. Правило простое: плагин, который не используется, выключают, а не «оптимизируют».
OPcache: самый дешёвый выигрыш
OPcache хранит скомпилированный байткод PHP в памяти и снимает повторную компиляцию. На типичном сайте это даёт заметный прирост без единой строки в шаблонах.
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=32
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
Настройки кладут в /etc/php/8.4/fpm/conf.d/99-opcache.ini или аналогичный файл вашей версии PHP. validate_timestamps=0 означает, что PHP не проверяет изменения файлов на каждом запросе. После деплоя обязательно перезагрузите FPM, иначе сайт будет работать на старом коде.
php-fpm8.4 -i | grep -E "opcache.(enable|memory_consumption|max_accelerated_files)"
sudo systemctl reload php8.4-fpm
sudo systemctl status php8.4-fpm --no-pager
Проверяйте именно FPM, а не CLI: у них отдельные конфигурации и разные кэши. Если opcache.enable в выводе равен Off, модуль не подключён или файл настроек лежит не в том каталоге.
Страничный кэш на стороне nginx
Готовый HTML можно отдавать без запуска PHP. Это самый большой выигрыш для сайта с преобладанием анонимных посетителей. В http-блок описывают зону кэша и ключ, а каталог /var/cache/nginx/wp создают заранее и передают nginx.
fastcgi_cache_path /var/cache/nginx/wp levels=1:2 keys_zone=wp:100m inactive=60m max_size=2g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
map $http_cookie $skip_cache {
default 0;
~*wordpress_logged_in 1;
~*wp-postpass 1;
~*comment_author 1;
~*woocommerce_items_in_cart 1;
}
map $request_method $skip_method {
default 1;
GET 0;
HEAD 0;
}
map $request_uri $skip_uri {
default 0;
~*^/(wp-admin|wp-login\.php|wp-cron\.php|xmlrpc\.php) 1;
}
Карта $skip_cache исключает авторизованных пользователей, комментаторов и корзину магазина. Второй map не даёт кэшировать POST-запросы, третий закрывает админку, вход, cron и XML-RPC. В блоке location ~ \.php$ остаётся подключить зону и добавить диагностический заголовок.
fastcgi_cache wp;
fastcgi_cache_valid 200 301 302 10m;
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_lock on;
fastcgi_cache_bypass $skip_cache $skip_method $skip_uri;
fastcgi_no_cache $skip_cache $skip_method $skip_uri;
add_header X-FastCGI-Cache $upstream_cache_status;
Заголовок X-FastCGI-Cache покажет HIT, MISS или BYPASS. Ответы с Set-Cookie nginx по умолчанию не кэширует — не отключайте это поведение без понимания последствий. Штатный nginx не умеет точечно сбрасывать кэш: либо короткий TTL, либо модуль ngx_cache_purge из сторонней сборки, либо очистка каталога вручную.
sudo nginx -t
sudo systemctl reload nginx
curl -sI https://example.ru/ | grep -i x-fastcgi-cache
sudo find /var/cache/nginx/wp -type f -delete
Объектный кэш и транзиенты
Страничный кэш не помогает авторизованным пользователям и динамическим блокам. Здесь нужен объектный кэш: Redis или Valkey плюс один drop-in object-cache.php. Это не «плагин-бомба», а один слой, который заменяет обращения к базе чтением из памяти. На практике ставят пакет redis-server или valkey, расширение php-redis и drop-in, затем проверяют работу через WP-CLI и журнал PHP.
wp cache flush --path=/var/www/example.ru
wp transient delete --expired --path=/var/www/example.ru
wp option list --autoload=on --format=total_bytes --path=/var/www/example.ru
wp db optimize --path=/var/www/example.ru
Просроченные транзиенты и раздутые автозагружаемые опции — типичная причина медленных запросов. Третья команда показывает суммарный размер автозагружаемых опций в байтах, четвёртая оптимизирует таблицы. Если размер превышает несколько мегабайт, ищите плагин, который пишет в autoload на каждом запросе.
База, ревизии и WP-Cron
Ревизии записей незаметно раздувают таблицу wp_posts. Ограничьте их в wp-config.php и удалите лишнее через WP-CLI.
define('WP_POST_REVISIONS', 5);
define('DISABLE_WP_CRON', true);
wp post delete $(wp post list --post_type=revision --format=ids) --force
wp cron event run --due-now --path=/var/www/example.ru
DISABLE_WP_CRON отключает запуск задач при посещениях и переносит их в системный cron. Это снимает лишнюю нагрузку с PHP-FPM, но требует настоящего планировщика.
*/5 * * * * cd /var/www/example.ru && /usr/bin/wp cron event run --due-now --path=/var/www/example.ru >> /var/log/wp-cron.log 2>&1
wp cron event list --path=/var/www/example.ru
Перед удалением ревизий сделайте резервную копию базы. Массовое удаление необратимо, а --force обходит корзину.
Статика, сжатие и заголовки
Последний слой — правильные заголовки для файлов, которые не меняются. Их отдаёт nginx, не пропуская запрос через PHP.
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
location ~* \.(css|js|jpg|jpeg|png|webp|avif|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
immutable уместен только для файлов с версией в имени: иначе браузер не увидит обновлённый стиль до истечения срока. Для HTML ставьте короткий Cache-Control или не задавайте его вовсе. Проверьте, что nginx отдаёт сжатые версии: curl -sI -H "Accept-Encoding: gzip" https://example.ru/style.css | grep -i content-encoding.
Порядок внедрения и чек-лист
- Снимите замеры TTFB и список активных плагинов до изменений.
- Включите OPcache и перезагрузите FPM, проверьте ответ сайта.
- Добавьте страничный кэш с обходом для авторизованных и POST.
- Подключите один объектный кэш, очистите просроченные транзиенты.
- Ограничьте ревизии и перенесите WP-Cron в системный планировщик.
- Настройте статику и сжатие, повторите замеры и сравните результат.
После каждого шага проверяйте вход в админку, оформление заказа и комментарии. Кэш, который ломает авторизацию, хуже отсутствия кэша. Держите простой способ отключения: убрать несколько строк из конфигурации nginx и перезагрузить сервис.
Итог
Ускорение WordPress без плагинов-бомб — это четыре независимых слоя: OPcache, страничный кэш nginx, объектный кэш и гигиена базы. Каждый даёт понятный эффект и легко откатывается. Начните с замеров и OPcache, затем добавьте кэш HTML с аккуратными исключениями, а плагины оставьте только те, без которых сайт действительно не работает.