TTFB в WordPress почти всегда упирается в то, как быстро сервер начинает отдавать первый байт ответа клиенту. Для динамических страниц это означает: PHP успел обработать запрос, хотя бы частично, а не застрял на загрузке PHP-классов, вычислениях и долгих SQL-запросах. Когда TTFB высокий, браузер ждёт, а контент появляется позже — даже если в итоге страница открывается нормально.

На VPS обычно выигрывает связка из двух уровней кэша. Nginx даёт page cache на уровне FastCGI (кешируем ответ PHP), а Redis — object cache (кешируем результаты дорогих вычислений и запросов в WordPress). Дополнительно снижают время ответа настройки PHP-FPM и OPcache: они уменьшают стоимость запуска PHP на каждый запрос.

Ниже — практичный план, как настроить Nginx и Redis, чтобы уменьшить TTFB, не сломав логины, админку и комментирование.

Почему в WordPress на VPS растёт TTFB

TTFB (Time To First Byte) — это не «сколько грузится страница целиком», а момент, когда сервер впервые начал отправлять данные. В WordPress на TTFB влияет цепочка: обработка запроса Nginx → запуск PHP-FPM → выполнение WordPress → база данных → генерация HTML → отдача ответа.

Чаще всего TTFB растёт по одной из причин:

  • Нет кэширования и каждый запрос проходит через весь стек WordPress.
  • PHP запускается слишком долго (нет OPcache или он настроен слабо).
  • PHP-FPM перегружен: очередь растёт, и запросы ждут свободных воркеров.
  • База данных делает лишнюю работу: повторяющиеся запросы без object cache.
  • Неправильно настроен reverse proxy: например, кешировать начали то, что нельзя (куки/логины/админка), и в итоге получили постоянные обходы кеша.

Важный момент: кеширование часто не «ускоряет всё сразу», оно меняет характер нагрузки. При page cache динамика превращается в быстро отдаваемые готовые ответы. При object cache WordPress перестаёт заново вычислять те же данные на каждом запросе.

Базовая архитектура кэширования: что делает Nginx, а что Redis

Чтобы уменьшить TTFB, нужно понять, какие части запроса кэшируются на разных уровнях:

  • Nginx page cache (fastcgi_cache)
  • Кешируется итоговый HTML-ответ, который возвращает index.php.
  • Для пользователя это выглядит как очень быстрый ответ, потому что PHP вообще не запускается (или запускается реже, только для промахов кэша).
  • Главный риск — кешировать страницы, которые нельзя кешировать для всех (например, для авторизованных пользователей).
  • Redis object cache
  • Кешируются промежуточные данные WordPress: результаты запросов, фрагменты вычислений, метаданные.
  • Это снижает время выполнения PHP, даже если page cache не сработал.
  • Главный риск — неправильные настройки соли ключей (при миграциях/разных средах) или включение Redis там, где его нельзя использовать.
  • OPcache и PHP-FPM
  • OPcache уменьшает стоимость загрузки и парсинга PHP-кода.
  • PHP-FPM помогает не уходить в очереди: количество воркеров и лимиты нужно подобрать под VPS.
  • Это влияет на TTFB особенно заметно при cache miss.

Хорошая практика — сначала стабилизировать PHP (OPcache + адекватные лимиты PHP-FPM), затем включить Redis, и только потом подключать агрессивный page cache в Nginx.

Настройка Redis как object cache для WordPress

Redis для WordPress обычно подключают через плагин, который умеет перехватывать кеши ядра и использовать Redis как хранилище. Самый частый сценарий — установить плагин наподобие WP Redis (или аналогичный, который поддерживает Redis object cache), а на сервере развернуть Redis и PHP-расширение.

  • Подготовьте сервер
  • Убедитесь, что Redis запущен и доступен с нужного хоста (локально на VPS это проще всего).
  • Установите PHP расширение Redis (обычно это пакет redis для вашей версии PHP).
  1. Подключите object cache в WordPress

В интерфейсе плагина обычно есть опции:

  • Хост/порт Redis (или socket).
  • Включение object cache.
  • Настройки префикса/ключа (иногда это называется cache key salt).

Если вы делаете тестовый стенд рядом с продом, важно не смешивать ключи между средами. Для этого используют соли ключей, отдельные префиксы или разные database в Redis. Ошибка смешивания приводит к «странным» эффектам: страница может выглядеть не так, как ожидается, или меняться не сразу.

  1. Проверьте, что кеш реально используется

После включения Redis важно убедиться, что WordPress записывает и читает из Redis. Это обычно видно в логах плагина, в отладочных выводах, а также по резкому уменьшению числа одинаковых запросов к базе при повторных обращениях.

  • Учтите нюансы WordPress
  • Не всегда имеет смысл кешировать всё без разбора. Для страниц, которые зависят от авторизации, часто корректнее полагаться на page cache с обходами или вовсе отключать page cache на такие сценарии.
  • Если у вас есть плагины для персонализации (каталог «для авторизованных», подписки, корзина), они могут добавлять куки или менять поведение ответа. Для них page cache в Nginx придётся обходить.

Redis в первую очередь ускоряет выполнение PHP, а это напрямую влияет на TTFB при cache miss.

Кэширование страниц через Nginx fastcgi_cache: что кешировать и как отключать

Nginx fastcgi_cache позволяет кешировать ответы, которые возвращает PHP-FPM. В WordPress это обычно значит кеширование для index.php и front controller. Если настроить аккуратно, TTFB для закешированных страниц будет очень низким, потому что Nginx отдаёт готовый ответ без запуска PHP.

Ключевой принцип: кешировать только то, что одинаково для большинства пользователей и запросов.

1) Настройте cache zone и путь

Пример директив для /etc/nginx/nginx.conf или отдельного файла в конфиге:

«`nginx fastcgicachepath /var/cache/nginx/fastcgi levels=1:2 keyszone=WORDPRESS:100m inactive=60m maxsize=2g; «`

Здесь:

  • levels разбивает директории, чтобы тысячи файлов не лежали в одной папке.
  • keys_zone задаёт размер памяти под индексы.
  • inactive и max_size защищают от неконтролируемого роста.

Важно: выделите достаточно места на диске VPS. Когда диск заполнится, кеш начнёт работать хуже или перестанет обновляться.

2) Включите fastcgi_cache в обработчике PHP

В типичном WordPress Nginx-конфиге есть location ~ \.php$.

Пример:

«`nginx location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/run/php/php-fpm.sock; fastcgi_index index.php;

fastcgi_cache WORDPRESS; fastcgicachevalid 200 301 302 60m; fastcgicachevalid 404 1m;

fastcgicachemin_uses 1; fastcgicacheusestale error timeout invalidheader updating http500 http503;

addheader X-FastCGI-Cache $upstreamcache_status always;

fastcginocache $skip_cache; fastcgicachebypass $skip_cache;

fastcgicachekey «$scheme$requestmethod$host$requesturi»; } «`

Пояснения по параметрам:

  • fastcgicachevalid задаёт, какие коды кешируются и на сколько.
  • X-FastCGI-Cache поможет понять, попали вы в кеш или нет.
  • fastcgicacheuse_stale позволяет отдавать устаревший контент при проблемах с PHP или upstream. Это полезно для стабильности, но требует аккуратного понимания ваших требований к актуальности.

fastcgicachekey включает requesturi, то есть запросы с разными query string будут кешироваться отдельно. Это безопасно, но может привести к фрагментации кеша. Если у вас query string в основном рекламный (utm*), можно настроить обход/нормализацию, но делать это стоит осторожно: легко получить ситуацию, когда разные параметры дают разные ответы, но вы их склеили в один кеш.

3) Определите $skip_cache, чтобы не кешировать то, что персонализируется

В WordPress есть очевидные категории, где page cache в Nginx ломает логику, если кешировать вслепую:

  • админка и логин
  • запросы для авторизованных пользователей
  • запросы с определёнными куками
  • любые методы кроме GET/HEAD
  • иногда — запросы с query string (зависит от ваших сценариев)

Типовой подход: задать переменную $skip_cache и включать её при условиях обхода.

Пример логики:

«`nginx set $skip_cache 0;

  • if ($request_method !~ ^(GET: HEAD)$) {

set $skip_cache 1; }

if ($request_uri ~* «^/wp-admin/») { set $skip_cache 1; }

  • if ($request_uri ~* «^/wp-(login: register)\.php») {

set $skip_cache 1; }

  • if ($httpcookie ~* «(commentauthor_: wordpressloggedin_; wp-postpass_)» ) {

set $skip_cache 1; }

if ($http_authorization != «») { set $skip_cache 1; } «`

Для куки логики важны реальные имена. Они зависят от того, что используют плагины и темы. Часто достаточно wordpressloggedin_, но если вы добавили свои плагины, проверьте, какие куки они устанавливают. Неправильный набор — одна из главных причин «почему кеш либо всегда мимо, либо сломал персонализацию».

4) Обработка front controller (try_files)

Чтобы WordPress корректно работал с Nginx, обычно используют try_files в location /:

«`nginx location / { try_files $uri $uri/ /index.php?$args; } «`

В этом случае PHP получает запрос фронт-контроллером. Page cache тогда будет кешировать итог ответа index.php.

5) Ограничьте кеширование на основе query string (по необходимости)

Если вы видите, что кеш фрагментируется на сотни тысяч вариантов, придётся корректировать $skipcache под querystring. Однако делать это нужно на основе наблюдений: какие запросы реально приходят и что они меняют в ответе.

Практичный подход:

  • начать с осторожного кеширования, где request_uri содержит query string
  • посмотреть статистику попаданий в кеш
  • затем точечно исключить «мусорные» параметры или разрешить кешировать с ними

Параметры PHP-FPM и OPcache для снижения TTFB

Даже при включённом page cache Nginx будут происходить промахи. На промахах TTFB снова становится чувствительным к тому, насколько быстро PHP стартует, насколько быстро он отдаёт результат и не простаивает ли под очередями.

1) OPcache: минимизируйте стоимость интерпретации PHP

OPcache уменьшает время выполнения за счёт хранения скомпилированного байткода PHP.

Пример базовых настроек (значения примерные, выбирайте под вашу память и версию PHP):

«`ini opcache.enable=1 opcache.memory_consumption=128 opcache.internedstringsbuffer=16 opcache.maxacceleratedfiles=10000 opcache.validate_timestamps=1 opcache.revalidate_freq=2 opcache.fast_shutdown=1 «`

Для production обычно:

  • validate_timestamps включают, чтобы изменения PHP подтягивались без полного рестарта (если не хотите рестарт при каждом деплое, это удобно).
  • revalidate_freq можно подстроить: слишком частая проверка директорий вернёт нагрузку назад.

2) PHP-FPM: подбирайте pm и лимиты под нагрузку

С PHP-FPM частая проблема выглядит так: кеш работает, но когда приходит пиковый период, TTFB растёт резко. Это типичная очередь воркеров.

Что обычно проверяют:

  • pm (static/dynamic/ondemand) и pm.max_children
  • лимиты по времени выполнения и размеру файлов
  • наличие лишних long-running запросов (например, тяжёлые плагины, которые не кэшируются)

Практика:

  • подберите pm.max_children так, чтобы VPS не уходил в swap
  • следите за показателями busy/wait (если у вас есть мониторинг)
  • если запросы часто «дожидаются» свободных воркеров, увеличивайте число воркеров или уменьшайте длительность запросов (через кэш и оптимизацию SQL)

Если вы видите, что даже без нагрузки воркеры заняты подолгу — проблема может быть не в количестве, а в том, что WordPress выполняет слишком много тяжёлых вычислений. Тут особенно помогает Redis object cache.

3) Убедитесь, что Nginx и PHP-FPM связаны корректно

Если вы используете unix socket, проверьте:

  • права на сокет
  • правильный user/group в конфигурациях
  • отсутствие узких мест на файловой системе с медленным диском

Иногда VPS на дешёвом HDD показывает «странный» TTFB из-за задержек на диске при чтении скриптов. В таком случае OPcache особенно критичен.

Продумываем инвалидирование кэша и кеш-подстановки

Кэширование на WordPress почти всегда упирается не в включение, а в обновление. Вы можете добиться низкого TTFB сегодня, но если публикации и правки не отражаются вовремя — получите жалобы.

1) Что именно нужно инвалидировать

Для связки Nginx + Redis обычно инвалидируют:

  • page cache в Nginx для URL публикаций/страниц, которые изменились
  • object cache в Redis, хотя он обычно обновляется через механизмы WordPress (в зависимости от плагина и типа данных)

На практике:

  • страница должна начать отдавать новый HTML после публикации
  • архивы и главная тоже часто требуют обновления, потому что они собираются из списка материалов

2) Поддержка purge в Nginx

Чтобы не ждать естественного TTL, удобен механизм purge. Он может быть реализован так, чтобы WordPress или плагин кэша отправлял запрос на endpoint, который очищает конкретный ключ в fastcgi_cache.

Схема обычно такая:

  • вы добавляете обработчик метода PURGE или отдельный путь
  • плагин вызывает очистку для нужного URL
  • Nginx удаляет соответствующие записи fastcgi_cache

В конфигурации обычно используют модуль nginx fastcgicachepurge (он может потребовать сборки или наличия пакета в вашей ОС). Если у вас нет возможности подключить purge на сервере, придётся жить с TTL или чистить кеш иначе (например, пересобирая/перезапуская кеш-слой, что менее удобно).

3) TTL: найдите баланс

fastcgicachevalid задаёт время жизни кеша. Слишком длинный TTL:

  • уменьшает нагрузку и TTFB, но задерживает обновления

Слишком короткий TTL:

  • быстрее обновляет контент, но чаще возвращает запросы в PHP и увеличивает TTFB на промахах

Для многих сайтов удобнее делать «умеренный» TTL и подключить purge при публикациях. Так вы получаете и актуальность, и стабильность TTFB.

4) Сложные случаи: пагинация, фильтры, языки

Если у вас есть:

  • пагинация (страницы /page/2/)
  • фильтры каталога с параметрами в query string
  • многоязычность

…то нужно убедиться, что cache key действительно различает ответы. Иначе обновления одного языка могут начать влиять на другой, либо разные параметры фильтра окажутся «склеены» в один кеш.

Проверка эффекта: как измерять TTFB и отлаживать кеширование

Без измерений легко включить кеширование «на бумаге» и не заметить, что Nginx всё время обходится или что fastcgi_cache не пишет записи.

1) Измерьте TTFB curl’ом

Удобная команда:

«`bash curl -s -o /dev/null -w «TTFB: %{time_starttransfer}\n» https://example.com/ «`

Повторите несколько раз:

  • первые запросы покажут cache miss (если кеш ещё не прогрет)
  • затем должны появиться быстрые ответы (cache hit)

2) Проверьте заголовок X-FastCGI-Cache

Если вы добавили addheader X-FastCGI-Cache $upstreamcache_status, то в ответе будет значение вроде:

  • HIT
  • MISS
  • BYPASS
  • EXPIRED

Если постоянно MISS или BYPASS, есть причины:

  • $skip_cache всегда выставляется в 1
  • метод не GET/HEAD
  • админские URL попадают в кеш
  • куки слишком активно обходят кеш

3) Сравните нагрузку на PHP-FPM и базу

После включения page cache:

  • PHP-FPM должен начать получать заметно меньше запросов
  • база данных будет разгружена
  • время выполнения WordPress на промахах может снизиться благодаря Redis object cache

Если PHP-FPM продолжает обрабатывать всё как раньше, значит page cache не работает так, как вы ожидаете.

4) Отдельно проверьте авторизованных пользователей

Откройте:

  • страницу от залогиненного пользователя
  • комментарии и элементы, зависящие от авторизации
  • админку

Цель — убедиться, что для них page cache обходится корректно. Если нет, то сначала правьте условия $skip_cache.

Типичные ошибки при кэшировании WordPress на VPS

Ниже ошибки встречаются чаще всего и обычно дают симптомы, которые выглядят как «кашица», но на деле решаются конфигом.

  1. Кэш не попадает из-за слишком строгих обходов

Слишком широкие if по cookie или request_uri с query string могут запретить кеширование почти всегда. Симптом: TTFB почти не меняется, а X-FastCGI-Cache всегда MISS/BYPASS.

  1. Кэшируются страницы для авторизованных

Если не учесть куки wordpressloggedin_ или не отключить wp-admin/wp-login, пользователи увидят чужие версии страниц. Это не только про удобство, это про безопасность и корректность контента.

  1. Неправильный fastcgicachekey

Если ключ не различает важные параметры (host, схема, языки), вы можете получить «перемешивание» контента. Если ключ слишком детальный, вы получите фрагментацию и кеш почти не будет попадать в hit.

  1. Redis включён, но не помогает

Иногда Redis настроен, но object cache не реально работает (не тот плагин, не те значения, key salt смешан, расширение PHP не подключилось). Симптом: SQL не сокращается, TTFB не улучшается при повторных запросах.

  1. Нет OPcache и PHP-FPM перегружен

Даже при page cache в моменты промахов TTFB будет высоким. Если OPcache выключен, WordPress будет дольше запускать и выполнять PHP на новых запросах.

  1. Кеш обновляется «через TTL», и контент устаревает

Если purge не настроен и TTL большой, публикации могут отображаться не сразу. Это часто обсуждают в комментариях к сайту, и обычно решается внедрением очистки fastcgi_cache на события WordPress.

Чек-лист внедрения и следующий шаг

Ниже порядок, который обычно даёт ожидаемый результат по TTFB без длинных итераций «проверка → поломка → откат».

  • Подготовьте базу
  • Включите OPcache.
  • Настройте PHP-FPM так, чтобы при нагрузке не росла очередь воркеров.
  • Убедитесь, что Redis доступен локально с WordPress.
  • Подключите Redis object cache
  • Включите object cache в плагине Redis.
  • Настройте ключи/префиксы так, чтобы prod и staging не смешивались.
  • Проверьте повторные запросы: база и время выполнения PHP должны снизиться.
  • Включите Nginx fastcgi_cache аккуратно
  • Добавьте fastcgicachepath и zone.
  • Включите fastcgi_cache для PHP location.
  • Реализуйте $skip_cache для wp-admin, логинов, куки авторизации и методов кроме GET/HEAD.
  • Добавьте X-FastCGI-Cache для диагностики.
  • Прогрейте кеш и измерьте TTFB
  • Сделайте несколько последовательных запросов к важным URL.
  • Убедитесь, что X-FastCGI-Cache показывает HIT после первых запросов.
  • Сравните TTFB по тем же URL.
  • Настройте инвалидирование
  • Определите, как будут обновляться кеши при публикациях и правках.
  • Если доступно, внедрите purge для fastcgi_cache.
  • Если purge нет, выберите TTL так, чтобы пользователи не видели устаревший контент слишком долго.

Итог: как добиться низкого TTFB на WordPress в связке Nginx и Redis

Рабочая схема для WordPress на VPS выглядит так: Nginx уменьшает стоимость ответов за счёт page cache, Redis сокращает время выполнения PHP за счёт object cache, а OPcache и PHP-FPM стабилизируют промахи кэша. Когда вы делаете это последовательно и с правильными условиями bypass, TTFB становится предсказуемым: быстрым на hit и управляемым на miss.

Следующий практичный шаг: выберите 5–10 ключевых URL (главная, типовые страницы, статья, архив), измерьте TTFB до изменений, затем включите Redis и Nginx fastcgicache, прогрейте кеш и повторите замеры. Если X-FastCGI-Cache показывает HIT и TTFB на этих URL падает — вы двигаетесь в правильную сторону. Если нет, обычно причина в логике $skipcache или в настройках cache key: это место, где стоит искать первым делом.