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).
- Подключите object cache в WordPress
В интерфейсе плагина обычно есть опции:
- Хост/порт Redis (или socket).
- Включение object cache.
- Настройки префикса/ключа (иногда это называется cache key salt).
Если вы делаете тестовый стенд рядом с продом, важно не смешивать ключи между средами. Для этого используют соли ключей, отдельные префиксы или разные database в Redis. Ошибка смешивания приводит к «странным» эффектам: страница может выглядеть не так, как ожидается, или меняться не сразу.
- Проверьте, что кеш реально используется
После включения 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
Ниже ошибки встречаются чаще всего и обычно дают симптомы, которые выглядят как «кашица», но на деле решаются конфигом.
- Кэш не попадает из-за слишком строгих обходов
Слишком широкие if по cookie или request_uri с query string могут запретить кеширование почти всегда. Симптом: TTFB почти не меняется, а X-FastCGI-Cache всегда MISS/BYPASS.
- Кэшируются страницы для авторизованных
Если не учесть куки wordpressloggedin_ или не отключить wp-admin/wp-login, пользователи увидят чужие версии страниц. Это не только про удобство, это про безопасность и корректность контента.
- Неправильный fastcgicachekey
Если ключ не различает важные параметры (host, схема, языки), вы можете получить «перемешивание» контента. Если ключ слишком детальный, вы получите фрагментацию и кеш почти не будет попадать в hit.
- Redis включён, но не помогает
Иногда Redis настроен, но object cache не реально работает (не тот плагин, не те значения, key salt смешан, расширение PHP не подключилось). Симптом: SQL не сокращается, TTFB не улучшается при повторных запросах.
- Нет OPcache и PHP-FPM перегружен
Даже при page cache в моменты промахов TTFB будет высоким. Если OPcache выключен, WordPress будет дольше запускать и выполнять PHP на новых запросах.
- Кеш обновляется «через 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: это место, где стоит искать первым делом.

