Страница пришла из кэша nginx за 30 миллисекунд, а сетка товаров на ней появляется через две секунды. Потому что сетка — это блок, который страница догружает отдельным запросом. И для nginx этот запрос не кэшируется вообще.
Это первая часть цикла «Кэш фрагментов и Redis». Здесь — всё про блоки, которые страница догружает ajax-запросами: как держать их в кэше так же надёжно, как страницы. О кэше самих страниц — цикл «Кэш страниц без MISS».
Почему nginx не кэширует ajax-блоки
В WordPress блоки обычно грузятся через admin-ajax.php. Это POST-запрос, и WordPress отвечает на него с Cache-Control: no-store. Оба признака для nginx значат «не кэшировать». И правильно: через тот же адрес идут отправка форм, лайки, корзина.
Цена — каждый показ блока собирается заново:
- сетка на 24 карточки — около 2 секунд;
- переход на следующую страницу каталога — 2,2 секунды.
После того как у блоков появился свой кэш, переход на следующую страницу стал занимать 0,2 секунды.
Главный приём: чистая функция рендера
Чтобы блок можно было и отдать посетителю, и прогреть фоновым воркером, его сборка выносится в функцию, которая не знает ни про $_POST, ни про вывод:
/** Принимает параметры, возвращает готовую разметку. Без $_POST и echo. */
function grid_render_fragment(array $args): array {
$q = new WP_Query([
'post_type' => 'product',
'paged' => (int) ($args['page'] ?? 1),
'posts_per_page' => (int) ($args['count'] ?? 24),
'tax_query' => grid_tax_query($args),
]);
ob_start();
get_template_part('templates/grid', null, ['query' => $q]);
return ['html' => ob_get_clean(), 'pages' => $q->max_num_pages];
}
// ajax: разобрать запрос, спросить кэш, при нужде — отрендерить
add_action('wp_ajax_nopriv_grid', 'grid_ajax');
add_action('wp_ajax_grid', 'grid_ajax');
function grid_ajax() {
$args = grid_args_from_request($_POST); // только разрешённые поля
wp_send_json(frag_get_or_render('grid', $args, 'grid_render_fragment'));
}
Одна и та же функция работает и в ajax-запросе посетителя, и в фоновом воркере прогрева. Если рендер зависит от чего-то вне аргументов, воркер соберёт не то, что увидит посетитель, — несколько частей цикла именно об этом.
Что дальше
- как устроено чтение «отдай старое, обнови фоном» и почему версии надо хранить в значении, а не в ключе;
- что обязано входить в ключ блока;
- как воркер травит кэш, если рендер не изолирован;
- как собрать перечень ячеек автоматически и следить за полнотой;
- и как перенести всё это из базы в Redis.
Индикатор. Статус блока отдаётся в заголовке ответа — у нас X-Frag-Cache: HIT, STALE, MISS или BYPASS. Без него ни одну из следующих частей не проверить.
Страница быстрая, а блоки на ней появляются медленно?
Пришлите адрес — проверю, кэшируются ли у вас ajax-блоки.
// загружаю комментарии…