DEV Community

Андрей Викулов (VProger)
Андрей Викулов (VProger)

Posted on • Originally published at viku-lov.ru on

WordPress: ускоряем Nginx + PHP-FPM до TTFB меньше 300 мс

WordPress: ускоряем Nginx + PHP-FPM до TTFB меньше 300 мс

WordPress: ускоряем Nginx + PHP-FPM до TTFB меньше 300 мс

Если wordpress долго загружается nginx php-fpm , TTFB 2–5 секунд, CPU стабильно загружен, а плагинов немного — проблема почти всегда в конфигурации PHP-FPM, OPcache и отсутствии fastcgi_cache.

После настройки:

  • TTFB меньше 300 мс
  • CPU без перегруза
  • OPcache реально работает
  • Nginx кеширует анонимный трафик

Без Docker. Обычный VPS в prod.

Сниппеты по статье: OPcache для WordPress (php.ini) · PHP-FPM pool (pm dynamic) · Nginx fastcgi_cache для WordPress · Проверка TTFB (curl)


В чём проблема

Симптомы:

  • Страница открывается 2–5 секунд
  • curl -w "%{time_starttransfer}" показывает TTFB 1.8–3.5 сек
  • CPU 70–100%
  • top показывает php-fpm в топе процессов
  • Кеш-плагинов нет или они не решают проблему

Проверка TTFB (сниппет: curl TTFB):


curl -o /dev/null -s -w "TTFB: %{time\_starttransfer}\n" https://example.com

Enter fullscreen mode Exit fullscreen mode

Если видите:


TTFB: 2.341221

Enter fullscreen mode Exit fullscreen mode

— PHP генерирует страницу каждый раз.

Nginx не кеширует.

OPcache либо отключён, либо настроен плохо.

Причины обычно такие:

  1. OPcache выключен или память 64MB.
  2. PHP-FPM работает с 5 процессами при 8 ядрах.
  3. Нет fastcgi_cache.
  4. Неправильно настроен bypass для админки.

Как отличить от других причин: если отключить все плагины (переименовать папку wp-content/plugins) и TTFB всё равно высокий — дело в стеке (Nginx/PHP-FPM/OPcache), а не в коде темы или плагинах. Если после отключения плагинов стало быстро — оптимизируйте тяжёлые плагины или кеш на уровне приложения. Здесь разбираем именно случай «плагинов немного, но стек не настроен».

WordPress сам по себе не медленный. Медленный — стек. Если после настройки появятся 502/504 — смотрите Nginx и PHP-FPM: ошибки 502, 504. Конфиг виртуальных хостов — в настройке server blocks для нескольких проектов.


Рабочее решение

1️⃣ Настраиваем OPcache (PHP 8.2)

Готовый блок с пояснениями: сниппет «OPcache для WordPress (php.ini)». Ниже — кратко.

Файл:


/etc/php/8.2/fpm/php.ini

Enter fullscreen mode Exit fullscreen mode

Ищем блок OPcache и приводим к нормальному виду:


opcache.enable=1

opcache.enable\_cli=0

opcache.memory\_consumption=256

opcache.interned\_strings\_buffer=32

opcache.max\_accelerated\_files=20000

opcache.validate\_timestamps=0

opcache.revalidate\_freq=0

opcache.fast\_shutdown=1

Enter fullscreen mode Exit fullscreen mode

Почему так:

  • 256MB — минимум для продакшена
  • validate_timestamps=0 — без постоянной проверки файлов
  • 20 000 файлов — WordPress + плагины легко съедают 10k+

Путь к php.ini может отличаться: на Debian/Ubuntu это обычно /etc/php/8.2/fpm/php.ini, на CentOS/Rocky — /etc/php.ini или отдельный файл в /etc/php.d/. Убедитесь, что правите именно конфиг FPM, а не CLI: php -i | grep "Loaded Configuration" покажет активный файл для CLI, для FPM смотрите конфиг пула.

Перезапуск:


systemctl restart php8.2-fpm

Enter fullscreen mode Exit fullscreen mode

Проверка:


php -i | grep opcache.enable

Enter fullscreen mode Exit fullscreen mode

Ожидаем:


opcache.enable => On => On

Enter fullscreen mode Exit fullscreen mode

2️⃣ Настройка PHP-FPM (CPU не должен кипеть)

Полный вариант с проверкой памяти: сниппет «PHP-FPM pool (pm dynamic)». Ниже — ключевые параметры.

Файл:


/etc/php/8.2/fpm/pool.d/www.conf

Enter fullscreen mode Exit fullscreen mode

Меняем pm режим:


pm = dynamic

pm.max\_children = 20

pm.start\_servers = 4

pm.min\_spare\_servers = 4

pm.max\_spare\_servers = 8

pm.max\_requests = 500

Enter fullscreen mode Exit fullscreen mode

pm.max_requests — после скольких запросов воркер перезапускается; снижает риск утечек памяти в долгоживущих процессах. 500–1000 нормально для WordPress.

Как считать:

  • max_children ≈ RAM / 60MB
  • Если 2GB RAM → ~30 процессов максимум

Проверка нагрузки:


ps --no-headers -o "rss,cmd" -C php-fpm8.2 | awk '{ sum+=$1 } END { print sum/1024 " MB" }'

Enter fullscreen mode Exit fullscreen mode

3️⃣ Включаем fastcgi_cache в Nginx

Это ключевой момент. Готовый конфиг с bypass для админки: сниппет «Nginx fastcgi_cache для WordPress».

Файл:


/etc/nginx/nginx.conf

Enter fullscreen mode Exit fullscreen mode

Добавляем в http{}:


fastcgi\_cache\_path /var/cache/nginx levels=1:2 keys\_zone=WORDPRESS:100m inactive=60m;

fastcgi\_cache\_key "$scheme$request\_method$host$request\_uri";

Enter fullscreen mode Exit fullscreen mode

Создаём директорию:


mkdir -p /var/cache/nginx

chown -R www-data:www-data /var/cache/nginx

Enter fullscreen mode Exit fullscreen mode

Размер зоны keys_zone=WORDPRESS:100m — это не место на диске, а объём ключей в памяти. 100m хватает на десятки тысяч URL. Реальный кеш лежит в /var/cache/nginx; убедитесь, что на разделе достаточно места (гигабайт и больше для активного сайта). Параметр inactive=60m — удалять ключ из кеша, если к нему не обращались 60 минут.


4️⃣ Настройка server{} для WordPress

Файл сайта:


/etc/nginx/sites-available/example.com

Enter fullscreen mode Exit fullscreen mode

В location ~ .php$ добавляем:


set $skip\_cache 0;

if ($request\_method = POST) {

set $skip\_cache 1;

}

if ($query\_string != "") {

set $skip\_cache 1;

}

if ($request\_uri ~\* "/wp-admin/|/xmlrpc.php|wp-login.php") {

set $skip\_cache 1;

}

if ($http\_cookie ~\* "wordpress\_logged\_in") {

set $skip\_cache 1;

}

location ~ \.php$ {

include fastcgi\_params;

fastcgi\_pass unix:/run/php/php8.2-fpm.sock;

fastcgi\_param SCRIPT\_FILENAME $document\_root$fastcgi\_script\_name;

fastcgi\_cache WORDPRESS;

fastcgi\_cache\_valid 200 60m;

fastcgi\_cache\_bypass $skip\_cache;

fastcgi\_no\_cache $skip\_cache;

add\_header X-FastCGI-Cache $upstream\_cache\_status;

}

Enter fullscreen mode Exit fullscreen mode

Перезапуск:


systemctl restart nginx

Enter fullscreen mode Exit fullscreen mode

Проверка результата

Проверка TTFB


curl -I https://example.com

Enter fullscreen mode Exit fullscreen mode

В заголовках должно быть:


X-FastCGI-Cache: MISS

Enter fullscreen mode Exit fullscreen mode

Повторный запрос:


X-FastCGI-Cache: HIT

Enter fullscreen mode Exit fullscreen mode

Проверка времени:


curl -o /dev/null -s -w "TTFB: %{time\_starttransfer}\n" https://example.com

Enter fullscreen mode Exit fullscreen mode

Ожидаемый результат:


TTFB: 0.112341

Enter fullscreen mode Exit fullscreen mode

CPU должен упасть минимум в 2–4 раза.

Краткий чеклист:

  • OPcache: php -i | grep opcache.enable → On
  • PHP-FPM: в pool.d/www.conf стоит pm = dynamic, pm.max_children по памяти
  • Nginx: в ответе есть заголовок X-FastCGI-Cache, при повторном запросе — HIT
  • TTFB: для закешированной главной — меньше 0,3 сек (лучше 0,05–0,15)

Если хотя бы один пункт не выполняется — возвращайтесь к соответствующему шагу выше. На проде после любых изменений конфига делайте nginx -t перед systemctl reload nginx.


Если не работает

1️⃣ Нет заголовка X-FastCGI-Cache

→ location блок не применяется.

Проверь:


nginx -T | grep fastcgi\_cache

Enter fullscreen mode Exit fullscreen mode

2️⃣ Всегда BYPASS

→ Cookie не даёт кешировать.

Проверь:


curl -I https://example.com | grep Set-Cookie

Enter fullscreen mode Exit fullscreen mode

3️⃣ OPcache не даёт прироста

→ validate_timestamps включён.

Проверь:


php -i | grep validate\_timestamps

Enter fullscreen mode Exit fullscreen mode

Должно быть 0 в проде. После деплоя кода делайте systemctl reload php8.2-fpm, чтобы OPcache подхватил новые файлы.

4️⃣ Кеш не создаётся (всегда MISS)

→ путь к кешу неверный или нет прав.

Проверь:


ls -la /var/cache/nginx

Enter fullscreen mode Exit fullscreen mode

Владелец должен быть www-data (или пользователь nginx). Если директории нет — создай и выдай права, как в шаге 3 выше, затем systemctl restart nginx.


Типичные ошибки

❌ OPcache 64MB

Причина: дефолт Debian

Решение: минимум 256MB


❌ pm = ondemand

Причина: экономия памяти

Проблема: запуск процессов под нагрузкой

Решение: dynamic


❌ Кеширование админки

Причина: нет bypass

Решение: skip_cache условия


❌ Кеш не чистится при деплое

Причина: validate_timestamps=0

Решение: после деплоя:


systemctl reload php8.2-fpm

Enter fullscreen mode Exit fullscreen mode

Где применять

  • Production VPS
  • Nginx + PHP-FPM
  • Без Docker
  • CI/CD с деплоем через git pull
  • WordPress 6.x

Если используешь Docker — логика та же, но путь к сокету другой. Основы Nginx — в Nginx: основы веб-сервера. Дальнейшая оптимизация фронта (LCP, INP, кеш) — WordPress и Core Web Vitals.


После обновления кода или темы

При opcache.validate_timestamps=0 OPcache не перечитывает файлы сам. После деплоя (git pull, загрузка обновлённой темы или плагинов) выполните:


systemctl reload php8.2-fpm

Enter fullscreen mode Exit fullscreen mode

Так PHP-FPM перезапустит воркеры и подхватит новый код. При необходимости сбросьте fastcgi_cache: удалите содержимое /var/cache/nginx или перезапустите Nginx. Для инвалидации только части кеша в Nginx нужна отдельная настройка (например, purge по ключу).


Итог

Если wordpress долго загружается nginx php-fpm , проблема почти никогда не в “тяжёлом WordPress”.

Проблема в:

  • выключенном OPcache
  • неправильном pm
  • отсутствии fastcgi_cache

После этих трёх шагов:

  • TTFB меньше 300 мс
  • CPU стабилен
  • Сайт ощущается быстрым

WordPress может работать быстро. Просто стек должен быть настроен как прод, а не как shared-хостинг 2012 года. Следуйте шагам по порядку и проверяйте результат после каждого этапа.

Read more on viku-lov.ru

Top comments (0)