Каждый раз, когда PHP-FPM обрабатывает запрос к WordPress, он открывает и парсит около 5000 файлов (ядро, плагины, тема). Без OPcache этот процесс повторяется при каждом HTTP-запросе, создавая огромную нагрузку на CPU и диск. OPcache решает эту проблему радикально: он кэширует скомпилированный байт-код в оперативной памяти, и PHP берёт готовые инструкции оттуда, минуя этап парсинга и компиляции.
На практике включение и правильная настройка OPcache даёт прирост производительности WordPress в 2-3 раза. Это самая простая и эффективная оптимизация серверной части, которая не требует изменений в коде приложения. Разбираем все аспекты: от базовой активации до продвинутого мониторинга и автоматической инвалидации при деплоях.
Что такое OPcache и как он работает
OPcache (Optimizer Cache) — встроенный в PHP модуль, начиная с версии 5.5. Его работа делится на три этапа:
- Первый запрос к файлу: PHP читает исходный
.php-файл с диска, парсит его в абстрактное синтаксическое дерево (AST), компилирует в байт-код (opcode) и сохраняет в общую память OPcache - Последующие запросы: PHP берёт готовый байт-код прямо из памяти, пропуская чтение диска и компиляцию. Это экономит 80-90% времени обработки
- Инвалидация: при изменении файла OPcache обнаруживает это (по timestamp или через принудительный сброс) и перекомпилирует только этот файл
Что именно кэшируется:
- Все файлы ядра WordPress (
wp-includes/,wp-admin/) - PHP-файлы активных плагинов и темы
- Библиотеки из
vendor/(Composer-зависимости) - Кастомные PHP-файлы в
functions.phpи дочерних темах
Что НЕ кэшируется:
- HTML-вывод (для этого нужен FastCGI Cache или плагин кэширования страниц)
- Результаты SQL-запросов (это задача Redis Object Cache)
- Статические файлы (CSS, JS, изображения — кэшируются Nginx)
Шаг 1: Проверка статуса OPcache
Прежде чем настраивать, убеждаемся, что OPcache активен. Через CLI:
php -v | grep -i opcache
# Ожидаемый вывод: with Zend OPcache v8.2.x
# Подробная информация о конфигурации
php -i | grep opcacheДля проверки через веб-интерфейс создаём временный файл opcache-status.php в корне сайта:
<?php
// Временный файл для проверки OPcache
if ( ! function_exists( 'opcache_get_status' ) ) {
die( 'OPcache не установлен или отключен' );
}
$status = opcache_get_status( false );
$config = opcache_get_configuration();
echo '<h1>OPcache Status</h1>';
echo '<h2>Общая информация</h2>';
echo '<pre>';
print_r( [
'enabled' => $status['opcache_enabled'],
'memory_usage' => round( $status['memory_usage']['used_memory'] / 1024 / 1024, 2 ) . ' MB из ' .
round( $status['memory_usage']['free_memory'] / 1024 / 1024, 2 ) . ' MB свободно',
'hit_rate' => round( $status['opcache_statistics']['opcache_hit_rate'], 2 ) . '%',
'cached_scripts' => $status['opcache_statistics']['num_cached_scripts'],
'misses' => $status['opcache_statistics']['misses'],
'hits' => $status['opcache_statistics']['hits'],
] );
echo '</pre>';⚠️ Важно: обязательно удалите этот файл после проверки. Он раскрывает информацию о конфигурации сервера и должен быть недоступен публично.
Шаг 2: Оптимальные параметры для WordPress
Стандартные настройки OPcache в php.ini рассчитаны на небольшие скрипты. Для WordPress с его тысячами файлов их нужно существенно увеличить. Открываем /etc/php/8.2/fpm/conf.d/10-opcache.ini (путь зависит от версии PHP и ОС):
; === Включение OPcache ===
opcache.enable = 1
opcache.enable_cli = 1 ; Включаем и для CLI (WP-CLI, cron)
; === Память ===
; WordPress + плагины требуют 256-512 МБ для байт-кода
opcache.memory_consumption = 256
; Строковый кэш (для интернированных строк)
opcache.interned_strings_buffer = 32
; === Лимит файлов ===
; WordPress с 30+ плагинами использует 8000-12000 PHP-файлов
; Значение должно быть степенью двойки + 1 (для хеш-таблиц)
opcache.max_accelerated_files = 16229
; === Стратегия инвалидации ===
; 0 = проверять файлы при каждом запросе (медленно, но всегда актуально)
; 60 = проверять раз в минуту (оптимально для продакшна)
; 300 = проверять раз в 5 минут (если деплой редкий)
opcache.revalidate_freq = 60
; === Оптимизации ===
; Сохранять комментарии в байт-коде (нужно для PHPDoc)
opcache.save_comments = 1
; Быстрое завершение (экономит немного CPU)
opcache.fast_shutdown = 1
; Проверка существования файла (предотвращает ошибки при удалении)
opcache.validate_timestamps = 1
; === Предзагрузка (PHP 7.4+) ===
; Предзагружает часто используемые файлы в память при старте FPM
; Требует создания preload-скрипта (см. Шаг 6)
; opcache.preload = /var/www/wordpress/opcache-preload.php
; opcache.preload_user = www-dataРасчёт оптимального memory_consumption:
Один PHP-файл в среднем занимает 20-30 КБ в OPcache. Формула:
# Подсчёт количества PHP-файлов в WordPress
find /var/www/wordpress -name "*.php" | wc -l
# Пример вывода: 11245
# Расчёт памяти: 11245 файлов × 25 КБ = ~275 МБ
# Округляем вверх до 300-512 МБ для запасаЕсли память OPcache закончится, новые файлы не будут кэшироваться, и hit rate упадёт. После изменения параметров обязательно перезагружаем PHP-FPM:
sudo systemctl restart php8.2-fpmШаг 3: Мониторинг hit rate
Hit rate — ключевая метрика эффективности OPcache. Показывает, какой процент запросов к файлам обслуживается из кэша, а не через компиляцию.
Целевые показатели:
- > 99%: идеальная настройка, кэш работает эффективно
- 95-99%: хорошо, но можно улучшить (увеличить memory_consumption или max_accelerated_files)
- < 95%: проблема, кэш переполнен или неправильно настроен
- < 50%: OPcache практически не работает
Скрипт для постоянного мониторинга:
#!/bin/bash
# /usr/local/bin/opcache-monitor.sh
# Получаем статус через WP-CLI (без создания публичных файлов)
STATUS=$(cd /var/www/wordpress && wp eval 'echo json_encode(opcache_get_status(false));' 2>/dev/null)
if [ -z "$STATUS" ]; then
echo "OPcache недоступен"
exit 1
fi
HIT_RATE=$(echo "$STATUS" | jq -r '.opcache_statistics.opcache_hit_rate' | cut -d. -f1)
USED_MEM=$(echo "$STATUS" | jq -r '.memory_usage.used_memory')
FREE_MEM=$(echo "$STATUS" | jq -r '.memory_usage.free_memory')
CACHED=$(echo "$STATUS" | jq -r '.opcache_statistics.num_cached_scripts')
MAX_FILES=$(echo "$STATUS" | jq -r '.opcache_statistics.max_cached_keys')
USED_MB=$((USED_MEM / 1024 / 1024))
FREE_MB=$((FREE_MEM / 1024 / 1024))
echo "=== OPcache Status ==="
echo "Hit Rate: ${HIT_RATE}%"
echo "Memory: ${USED_MB}MB used, ${FREE_MB}MB free"
echo "Scripts: ${CACHED} / ${MAX_FILES}"
# Алерт при низком hit rate
if [ "$HIT_RATE" -lt 95 ]; then
echo "⚠️ WARNING: Hit rate below 95%"
# Отправка в Telegram (настройка по аналогии со скриптом уведомлений о бэкапах)
curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
-d chat_id="$CHAT_ID" \
-d text="⚠️ OPcache Alert: hit_rate=${HIT_RATE}%, used=${USED_MB}MB"
fi
# Алерт при нехватке памяти
if [ "$FREE_MB" -lt 20 ]; then
echo "⚠️ WARNING: Less than 20MB free memory"
fiДобавляем в cron для проверки каждые 15 минут:
*/15 * * * * /usr/local/bin/opcache-monitor.sh >> /var/log/opcache-monitor.log 2>&1Шаг 4: Инвалидация кэша при деплое
Если opcache.revalidate_freq установлен в 60 секунд, изменения в PHP-файлах после деплоя появятся только через минуту. Для продакшна это критично — нужно сбрасывать OPcache сразу после выгрузки нового кода.
Метод 1: Через WP-CLI (рекомендуемый)
WP-CLI имеет встроенную команду для сброса OPcache. Добавляем её в конец CI/CD скрипта деплоя:
# После rsync или git pull
cd /var/www/wordpress
wp cache flush
wp eval 'if (function_exists("opcache_reset")) { opcache_reset(); echo "OPcache cleared\n"; }'Метод 2: HTTP-эндпоинт для сброса
Для удалённых деплоев через GitHub Actions создаём защищённый endpoint:
// В functions.php темы или в кастомном плагине
add_action( 'init', function() {
if ( ! isset( $_GET['opcache_reset'] ) ) {
return;
}
// Проверка секретного токена
if ( $_GET['opcache_reset'] !== CUSTOMCODE_OPCACHE_SECRET ) {
wp_die( 'Unauthorized', 403 );
}
if ( function_exists( 'opcache_reset' ) ) {
opcache_reset();
header( 'Content-Type: application/json' );
echo json_encode( [
'status' => 'success',
'message' => 'OPcache cleared',
'timestamp' => time(),
] );
exit;
}
wp_die( 'OPcache not available', 500 );
} );В wp-config.php добавляем секретный токен:
define( 'CUSTOMCODE_OPCACHE_SECRET', 'your-very-secret-token-here' );В GitHub Actions workflow добавляем вызов после деплоя:
- name: Clear OPcache
run: |
curl -s "https://custom-code.ru/?opcache_reset=${{ secrets.OPCACHE_SECRET }}"Метод 3: Перезагрузка PHP-FPM
Самый радикальный способ — полная перезагрузка пула FPM. OPcache хранится в общей памяти процесса, поэтому при перезапуске кэш полностью очищается:
sudo systemctl reload php8.2-fpmЭтот метод надёжен, но вызывает кратковременный всплеск нагрузки, так как все worker-процессы пересоздаются. Подходит для ночных деплоев, но не для продакшна в часы пик.
Шаг 5: OPcache Preloading (PHP 7.4+)
Preloading — продвинутая функция, которая загружает указанные PHP-файлы в память при старте PHP-FPM, до обработки первого запроса. Это устраняет даже первую компиляцию для критичных файлов.
Создание preload-скрипта:
Создаём файл /var/www/wordpress/opcache-preload.php:
<?php
/**
* OPcache Preload для WordPress
* Загружает ядро и активные плагины в память при старте FPM
*/
// Загружаем конфигурацию WordPress
require_once __DIR__ . '/wp-load.php';
// Список файлов для предзагрузки
$preload_files = [
// Ядро WordPress
__DIR__ . '/wp-includes/load.php',
__DIR__ . '/wp-includes/functions.php',
__DIR__ . '/wp-includes/class-wp.php',
__DIR__ . '/wp-includes/class-wp-query.php',
__DIR__ . '/wp-includes/post.php',
__DIR__ . '/wp-includes/query.php',
// Критичные плагины (замените на свои)
__DIR__ . '/wp-content/plugins/redis-cache/redis-cache.php',
__DIR__ . '/wp-content/plugins/query-monitor/query-monitor.php',
// Функции темы
__DIR__ . '/wp-content/themes/customcode/functions.php',
];
foreach ( $preload_files as $file ) {
if ( file_exists( $file ) ) {
opcache_compile_file( $file );
}
}Активация в php.ini:
opcache.preload = /var/www/wordpress/opcache-preload.php
opcache.preload_user = www-data⚠️ Важно: Preloading работает только для PHP-FPM, не для CLI. Изменения в preload-скрипте требуют полной перезагрузки FPM. На высоконагруженных сайтах preloading даёт дополнительные 5-10% прироста производительности.
Шаг 6: OPcache GUI для визуального мониторинга
Для наглядного отображения статуса OPcache используется opcache-gui — лёгкий веб-интерфейс на одной странице.
Установка:
# Скачиваем в защищённую директорию
sudo mkdir -p /var/www/opcache-gui
cd /var/www/opcache-gui
sudo wget https://raw.githubusercontent.com/amnuts/opcache-gui/master/index.php
# Настраиваем доступ только по IP администратора
sudo chown -R www-data:www-data /var/www/opcache-guiЗащита через Nginx:
GUI содержит детальную информацию о сервере, поэтому доступ должен быть ограничен:
location /opcache-gui/ {
alias /var/www/opcache-gui/;
index index.php;
# Только ваши IP
allow 1.2.3.4; # IP офиса
allow 5.6.7.8; # IP домашнего интернета
deny all;
# Базовая авторизация
auth_basic "OPcache GUI";
auth_basic_user_file /etc/nginx/.htpasswd;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
}После настройки GUI доступен по адресу https://custom-code.ru/opcache-gui/ и показывает графики hit rate, использования памяти и список закэшированных файлов.
Шаг 7: Сравнение OPcache с другими кэшами
OPcache — лишь один из слоёв кэширования в стеке WordPress. Понимание различий помогает избежать путаницы:
| Тип кэша | Что кэширует | Экономия |
|---|---|---|
| OPcache | Скомпилированный PHP байт-код | 2-3× ускорение PHP |
| Redis Object Cache | Результаты SQL-запросов, опции, transient’ы | 10-100× меньше запросов к БД |
| FastCGI Cache | Готовый HTML-вывод страниц | Полное исключение PHP для закэшированных страниц |
| Browser Cache | CSS, JS, изображения в браузере пользователя | Уменьшение трафика |
| CDN Cache | Статика на edge-серверах | Снижение нагрузки на origin |
Все эти кэши дополняют друг друга. Например, при запросе к закэшированной странице FastCGI Cache возвращает HTML вообще без запуска PHP. При промахе мимо FastCGI Cache запускается PHP, который использует OPcache для быстрого выполнения кода и Redis для кэширования результатов SQL-запросов.
Чек-лист настройки OPcache
- ✅ OPcache включён в
php.ini(opcache.enable = 1) - ✅
memory_consumptionустановлен в 256-512 МБ (рассчитан по количеству файлов) - ✅
max_accelerated_filesпревышает количество PHP-файлов в проекте - ✅
revalidate_freqустановлен в 60 секунд для продакшна - ✅
enable_cli = 1для работы OPcache в WP-CLI и cron - ✅ Настроен скрипт мониторинга hit rate с алертами
- ✅ Автоматическая инвалидация при деплое (через
opcache_reset()) - ✅ Рассмотрен вариант с preloading для критичных файлов
- ✅ Hit rate стабильно выше 99%
Решение частых проблем
Hit rate низкий (меньше 95%)
Три возможные причины:
- Недостаточно памяти: увеличьте
opcache.memory_consumption. Проверьте через GUI, сколько памяти свободно — если меньше 20%, нужно расширять. - Недостаточно слотов для файлов: увеличьте
opcache.max_accelerated_files. Если в проекте 10 000 PHP-файлов, а лимит 4000, часть файлов не будет кэшироваться. - Слишком частая инвалидация: если
revalidate_freq = 0, OPcache проверяет timestamp каждого файла при каждом запросе, что создаёт нагрузку на диск.
После деплоя изменения не видны
Если revalidate_freq = 60, OPcache будет использовать старый байт-код ещё минуту после обновления файлов. Решения:
- Добавить
opcache_reset()в скрипт деплоя - Перезагрузить PHP-FPM после деплоя
- Временно установить
revalidate_freq = 2на время активных разработок
OPcache вызывает ошибки «Cannot redeclare class»
Это признак конфликта при перекомпиляции файлов. Обычно возникает при:
- Использовании
eval()илиcreate_function()— такие конструкции плохо работают с OPcache - Условных объявлениях классов (например,
if ( ! class_exists() )) — при инвалидации кэша класс может быть объявлен дважды
Решение: рефакторинг кода с использованием автозагрузки PSR-4 вместо ручных require_once.
OPcache потребляет слишком много памяти
Если memory_consumption установлен в 1 ГБ, но реально используется только 100 МБ, это пустая трата RAM. Проверьте фактическое использование через GUI и уменьшите параметр с запасом 30-50% на рост проекта.
WP-CLI работает медленно, несмотря на OPcache
По умолчанию opcache.enable_cli отключён, поэтому WP-CLI не использует OPcache. Включение этого параметра ускоряет выполнение WP-CLI команд в 2-3 раза, что критично для скриптов автоматизации, массовых операций и cron-задач.
Итог
OPcache — обязательный компонент современного PHP-стека. Правильная настройка занимает 15 минут, а выигрыш в производительности колоссален. В связке с Redis Object Cache и FastCGI Cache он образует трёхуровневую систему кэширования, которая позволяет WordPress обрабатывать сотни запросов в секунду даже на скромных VPS. Регулярный мониторинг hit rate и автоматическая инвалидация при деплоях гарантируют, что кэш работает эффективно и не становится источником проблем с устаревшим кодом.