Без кэша каждый просмотр страницы WordPress запускает PHP: загружается ядро, каждый активный плагин, выполняется от тридцати до ста запросов к базе, рендерится тема. Для обычного сайта это от 300 до 900 миллисекунд серверного времени, и всё повторяется для следующего посетителя, запросившего ту же самую страницу. Кэш целых страниц хранит готовый HTML и отдаёт его, не трогая PHP, что снижает время до первого байта примерно до 20-80 миллисекунд. Ничто другое из доступного вам не сравнится с этим.
Всё остальное меньше и специфичнее: OPcache, чтобы PHP не перекомпилировал сам себя, объектный кэш для запросов, которые кэш страниц покрыть не может, дисциплина с изображениями, чтобы браузер скачивал меньше, и работа на стороне клиента, которую хостинг за вас сделать не может вовсе. Эта статья проходит по ним в том порядке, в котором они возвращают больше всего времени, и говорит, какое измерение доказывает, что каждое из них сработало.
Куда на самом деле уходит время#
Прежде чем что-либо менять, разделите загрузку страницы на часть, которой владеет сервер, и часть, которой владеет браузер. Первую половину curl даёт бесплатно:
$ curl -o /dev/null -s -w "dns %{time_namelookup} tcp %{time_connect} \tls %{time_appconnect} ttfb %{time_starttransfer} total %{time_total}\n" \ https://example.com/| Фаза | Типичное хорошее значение | Что это значит |
|---|---|---|
dns | 0.01-0.05 s | Запрос к резолверу, после первого запроса кэшируется |
tcp | 0.02-0.10 s | Круговой путь до сервера, по большей части расстояние |
tls | 0.04-0.15 s | Рукопожатие, ещё один-два круговых пути |
ttfb | 0.05-0.20 s | Всё перечисленное выше плюс время на размышления сервера |
total | меньше 1 s | Плюс загрузка самого HTML |
Вычтите tls из ttfb, и останется собственная работа сервера: PHP, база данных и файловая система. Только это число и меняет кэширование. Если ваш ttfb равен 0,6 с, а tls завершился на 0,12 с, у вас примерно полсекунды PHP, на которые можно наступать. Если ttfb равен 0,18 с, а страница всё равно кажется медленной, проблема в браузере, и никакой хостер её не исправит; эта граница подробнее проведена в статье TTFB, Core Web Vitals и хостинг.
Запустите запрос дважды. Второй скажет, работает ли кэш; первый покажет, что получает невезучий посетитель.
Кэш страниц - это большая часть выигрыша#
Кэш страниц хранит отрендеренный HTML для URL и отдаёт его напрямую при следующем запросе. Для блога, сайта-визитки или сайта с документацией это покрывает почти всех посетителей, потому что почти все посетители не вошли в систему и просят то, что уже просил кто-то другой.
В WordPress для этого есть встроенный хук. Строка define( 'WP_CACHE', true ); в wp-config.php заставляет ядро очень рано, раньше большей части фреймворка, загружать wp-content/advanced-cache.php, а плагин кэширования кладёт этот файл на место. Самые популярные - WP Super Cache, W3 Total Cache, Cache Enabler, LiteSpeed Cache (полезен только на сервере LiteSpeed) и WP Rocket. Выберите один. Два кэша страниц на одном сайте - классический способ показать панель администратора вошедшего пользователя всем посетителям.
Важнее остальных экранов плагина три настройки:
- Что обходит кэш. Любой запрос с cookie
wordpress_logged_in_*,comment_author_*,wp-postpass_*или, в магазине, cookie корзины должен обслуживаться заново. Каждый серьёзный плагин делает это по умолчанию. Если сделано неверно, проявляется не медленность, а то, что один пользователь видит страницу другого. - Срок жизни кэша. От нескольких часов до суток подходит большинству сайтов. Короткие сроки превращаются в постоянно холодный кэш на сайте с малым трафиком, где каждая страница устаревает раньше, чем её запросят дважды.
- Предзагрузка. Обход карты сайта для прогрева кэша после очистки. Полезна на сайте с сотнями страниц и умеренным трафиком; бессмысленна на загруженном, где посетители прогревают кэш за минуту.
Проверяйте снаружи, без входа в систему, в приватном окне или через curl -I. Большинство плагинов добавляют заголовок - x-cache: HIT, x-litespeed-cache: hit, комментарий внизу HTML. Если такой метки найти нельзя, сравните ttfb двух последовательных запросов к одному URL: у работающего кэша страниц виден очевидный обрыв.
Чем кэш страниц помочь не может: корзина, оформление заказа, страницы аккаунта, форум для вошедших пользователей, всё, что персонализировано для каждого посетителя. В магазине это значит, что самые важные для выручки страницы - именно те, что по-прежнему запускают PHP при каждом обращении, поэтому требования к хостингу для WooCommerce отличаются по природе, а не по степени.
OPcache#
PHP при каждом запросе компилирует исходный код в опкоды, если не включён OPcache, хранящий их в разделяемой памяти. WordPress с двадцатью плагинами - это тысячи файлов, поэтому это даёт от 30 до 50 процентов времени PHP на некэшированном запросе и не стоит ничего, кроме памяти.
opcache.enable=1opcache.memory_consumption=192opcache.interned_strings_buffer=16opcache.max_accelerated_files=20000opcache.validate_timestamps=1opcache.revalidate_freq=2| Настройка | Значение по умолчанию | Зачем менять |
|---|---|---|
opcache.memory_consumption | 128 (МБ) | Сайт с большим числом плагинов может заполнить 128 МБ; переполненный кэш молча перестаёт кэшировать |
opcache.interned_strings_buffer | 8 (МБ) | Повышается вместе с предыдущей на больших сайтах |
opcache.max_accelerated_files | 10000 | WordPress и 30 плагинов могут превысить 10 000 файлов |
opcache.validate_timestamps | 1 | Для WordPress оставьте включённым |
opcache.revalidate_freq | 2 (секунды) | Насколько устаревшим может отдаваться изменённый файл |
Единственное, чему стоит сопротивляться, - opcache.validate_timestamps=0. Это верная настройка для приложения, развёртываемого из Git с последующим перезапуском, и неверная для WordPress, где обновления плагинов и ядра меняют файлы изнутри админки. При выключенной проверке меток времени такие обновления как будто ничего не делают, пока PHP не перезапущен, а наполовину применённое обновление - это сломанный сайт.
Проверяйте, не переполнен ли OPcache, а не предполагайте. opcache_get_status() в однострочном скрипте или страница состояния, которая есть в большинстве плагинов кэширования, показывает num_cached_keys против max_cached_keys, used_memory против free_memory и долю попаданий. Доля попаданий ниже примерно 95 процентов на устойчивой нагрузке означает, что кэш вытесняется, а значит, одно из первых двух чисел слишком мало.
Объектный кэш и база данных#
У WordPress есть внутренний объектный кэш, хранящий результаты запросов и вычисленные значения под ключами. По умолчанию он живёт в рамках запроса: создаётся в начале запроса и выбрасывается в конце, поэтому экономит только повторную работу внутри одной загрузки страницы. Чтобы он стал постоянным, нужен drop-in в wp-content/object-cache.php поверх Redis или Memcached, который устанавливается плагином вроде Redis Object Cache.
Честно оцените, доступно ли это вам. Нужен сервер Redis или Memcached, до которого дотянется процесс PHP, и расширение для общения с ним. Во многих общих и контейнерных хостингах нет ни того ни другого - управляемые базы RE:NODE это PostgreSQL и MongoDB, которыми WordPress не пользуется, - поэтому, если вы не можете указать на реальный экземпляр, постоянный объектный кэш вам не светит, и хорошо настроенный кэш страниц становится соразмерно важнее. Общее решение описано в статье Redis и когда он на самом деле нужен.
Всегда можно сделать другое: перестать так много спрашивать у базы. Начните с автозагружаемых опций, которые читаются целиком при каждом запросе:
SELECT option_name, LENGTH(option_value) AS bytesFROM wp_optionsWHERE autoload IN ('yes', 'on')ORDER BY bytes DESCLIMIT 20;Конструкция IN нужна потому, что в WordPress 6.6 столбец autoload изменили с простых yes/no на набор, включающий on, off и auto, поэтому запрос, написанный под старые значения, молча вернёт пустоту на новой установке. Сложите столбец bytes: меньше нескольких сотен килобайт - здоровое состояние, больше мегабайта - налог на каждый просмотр страницы. Обычные виновники - плагин, хранящий большой кэшированный блок как автозагружаемую опцию, и годы осиротевших transient, оставленных плагинами, которые удалили без очистки.
Ревизии записей - другая лёгкая победа. Страница, отредактированная восемьдесят раз, несёт восемьдесят копий в wp_posts. Ограничьте их в wp-config.php, а не удаляйте вручную раз в год:
define( 'WP_POST_REVISIONS', 10 );define( 'EMPTY_TRASH_DAYS', 14 );Изображения - это большая часть байтов#
На типичной странице WordPress изображения составляют от 60 до 80 процентов передаваемого веса. Чтобы это исправить, сервер задействовать не нужно.
- Загружайте разумные оригиналы. WordPress уменьшает всё шире 2560 пикселей и хранит оригинал рядом с суффиксом
-scaled, поэтому фотография с телефона в 6000 пикселей стоит хранилища дважды и ничего не даёт. Уменьшайте до загрузки. - Используйте современный формат. WordPress принимает загрузки WebP с версии 5.8 и AVIF с версии 6.5. WebP обычно на 25-35 процентов меньше эквивалентного JPEG при том же визуальном качестве.
- Ленивая загрузка уже включена. С версии 5.5 WordPress добавляет
loading="lazy"к изображениям, а с версии 6.3 вместо этого помечает вероятное самое большое изображение какfetchpriority="high", что верно для Largest Contentful Paint. Плагин, который лениво загружает всё подряд, включая главное изображение, ухудшает LCP. - Перегенерируйте миниатюры после смены темы. Новая тема регистрирует другие размеры; без перегенерации браузеру отдаётся полноразмерный файл, уменьшенный средствами CSS.
- Следите за хранилищем. Каждый зарегистрированный размер умножает каждую загрузку. Пять размеров плюс оригинал плюс уменьшенная копия - это семь файлов на одну фотографию, и медиатека - самый большой потребитель диска на большинстве тарифов для WordPress.
Клиентская часть обычно медленнее#
Когда время до первого байта меньше 200 миллисекунд, оставшиеся секунды принадлежат браузеру, и именно здесь живёт большая часть «медленного WordPress». Хостинг ничего из перечисленного ниже не меняет.
- CSS и JavaScript, блокирующие отрисовку. Конструктор страниц, загружающий восемь таблиц стилей, jQuery и пять скриптов плагинов, прежде чем что-либо появится на экране.
- Шрифты. Три семейства в четырёх начертаниях, каждое отдельной загрузкой; размещайте их у себя, делайте подмножества и задавайте
font-display: swap. - Сторонние скрипты. Виджет чата, два тега аналитики, баннер согласия и пиксель. Каждый - это DNS-запрос, соединение и блокирующий скрипт, и они постоянно оказываются худшими виновниками по Interaction to Next Paint.
- Слайдеры и видео в шапке. Крупные, выше сгиба и обычно декоративные.
Измеряйте эту половину инструментом браузера, а не через curl. Полевые данные в отчёте Core Web Vitals не совпадут с вашим лабораторным тестом, и учитываются именно полевые. Другая половина скорости клиентской части - заставить браузер хранить уже скачанное, чтобы повторные просмотры ничего не стоили, - разобрана в статье заголовки HTTP-кэширования простыми словами.
Рабочие процессы PHP и ёмкость, о которой никто не говорит#
Скорость под нагрузкой - не то же самое, что скорость в простое. У пула PHP-FPM фиксированное число рабочих процессов, и каждый обслуживает один запрос за раз. Поэтому арифметика беспощадна:
requests per second = workers / average request secondsЧетыре рабочих процесса при 0,5 с на некэшированный запрос - это восемь запросов в секунду, и девятый посетитель встаёт в очередь. Добавьте кэш страниц, и те же четыре процесса обслуживают лишь небольшую долю запросов, не попавших в него, а остальные отдаются с диска за микросекунды. Именно поэтому кэширование важно на небольшом тарифе: оно меняет ёмкость на порядок, а не только задержку.
Число процессов ограничено памятью. Каждый рабочий процесс WordPress держит в памяти от 40 до 80 МБ, а иногда и больше с тяжёлой темой, так что 1 ГБ RAM поддерживает примерно от восьми до двенадцати процессов, когда веб-сервер и база получили свою долю. Значение pm.max_children выше того, что выдерживает память, не добавляет ёмкости, а превращает очередь в событие нехватки памяти. Эту арифметику как следует разбирает статья сколько RAM нужно WordPress, а настройки php.ini, которые важны описывают memory_limit - сторону того же вопроса, относящуюся к отдельному процессу.
Что меняет хостинг, а что нет#
Хостингу принадлежат четыре вещи, и стоит точно их назвать, потому что хостер не может продать вам более быструю тему.
- Доля CPU определяет, как быстро выполняется один запрос PHP. Жёсткое ограничение CPU означает, что сервер на пределе работает медленно, а не ломается.
- Память определяет, сколько запросов выполняется одновременно, через число рабочих процессов.
- Диск важен меньше, чем думают, когда OPcache прогрет, но NVMe сокращает холодные чтения и делает запись в базу дешёвой.
- Расстояние задаёт нижнюю границу для
tcpиtls. Немецкий сервер, отвечающий посетителям в Бразилии, платит 200 миллисекунд за каждый круговой путь, что бы вы ни кэшировали. Это довод в пользу CDN перед сервером, а не другого хостера.
В RE:NODE тарифы для сайтов работают на хранилище NVMe на оборудовании в Германии, CPU ограничивается долей, купленной тарифом, а не заимствуется у соседей, а консоль показывает вывод самого веб-сервера, так что ошибка 500 видна без обращения в поддержку. Если сайт всё же достигает лимита памяти, контейнер останавливается и перезапускается начисто, а не уходит в swap, - это резко, но делает машину предсказуемой и ещё одна причина подбирать число процессов под тариф, а не под оптимизм. Что говорят графики памяти и CPU в панели, пока дело не дошло до этого, объясняет статья чтение графика нагрузки сервера.
FAQ#
Какой плагин кэширования выбрать?
Любой из основных, настроенный один раз и оставленный в покое. WP Super Cache и Cache Enabler проще всех, W3 Total Cache настраивается глубже всех и проще всех неверно настраивается, LiteSpeed Cache - лучший выбор, только если сервер действительно работает на LiteSpeed. Разница между двумя хорошо настроенными кэшами страниц невелика; разница между одним и отсутствием кэша огромна.
Заменяет ли CDN кэш страниц?
Нет, они решают разные половины задачи. CDN приближает байты к посетителю и великолепно кэширует статические файлы, но если вы не настроили кэширование целых страниц на границе, он всё равно просит ваш исходный сервер строить каждую HTML-страницу. Используйте оба: кэш страниц делает исходный сервер дешёвым, CDN делает расстояние коротким.
Почему сайт быстрый для меня и медленный для посетителей?
Три причины, по убыванию вероятности: вы вошли в систему и пропускаете кэш, у вашего браузера всё уже закэшировано, и вы территориально близки к серверу. Тестируйте без входа в систему, в приватном окне, инструментом, который делает запросы из другого места.
Есть ли смысл в объектном кэше без Redis?
Постоянного объектного кэша без Redis или Memcached не бывает, а замены на основе базы данных обычно лишь переносят работу, а не убирают её. Потратьте силы на кэш страниц, на сокращение автозагружаемых опций и на удаление плагина, который выполняет сорок запросов на страницу.
Ускорит ли сайт больше RAM?
Только если её не хватает. Больше памяти даёт больше одновременных рабочих процессов PHP, поэтому она поднимает точку, в которой сайт замедляется под нагрузкой, но не ускоряет рендеринг одной страницы. Если медленно даже одному посетителю, ответы - OPcache, меньше запросов и более лёгкая тема.
Как найти плагин, который всё тормозит?
Установите Query Monitor, загрузите медленную страницу под администратором и прочтите количество запросов, самые медленные запросы и время на каждый хук. Он называет файл и плагин. Двадцать запросов на страницу - нормально, двести означает, что какой-то плагин делает глупости при каждом обращении.




Комментарии
Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.