Кэш-инвалидация в headless: как Next.js узнаёт, что Bitrix обновил товар
Первая жалоба пришла на третий день после запуска headless. Менеджер обновил цену в Bitrix в 10:47. Покупатель положил товар в корзину в 11:20 — по старой цене. В Next.js была страница, закэшированная на один час.
Технически всё работало. Бизнес был недоволен.
Мы потратили три часа не на отладку — а на то, чтобы объяснить, что это не баг. Это выбор, который мы сделали при проектировании. Просто никто об этом не предупредил заказчика заранее.
Откуда берётся протухший кэш в headless
В монолитном Bitrix проблемы нет: запрос пришёл, шаблон отработал, данные из базы актуальные. Кэш, конечно, тоже есть, но он на той же машине, с тем же доступом к базе, и инвалидируется Bitrix-ядром автоматически.
В headless архитектура другая. Bitrix — бэкенд с REST API. Next.js — фронтенд, который получает данные через HTTP и кэширует их сам. Цикл жизни данных разрывается: Bitrix обновил товар, но Next.js об этом не знает. Не потому что это неправильно сделано. Потому что в headless это структурная особенность.
На проекте с 28 000 SKU обновлений товаров было до 300 в день: цены, остатки, описания. Каждое обновление в Bitrix — это потенциальная расхождение с тем, что видит пользователь на фронте. Вопрос не «будет ли расхождение», а «как долго оно будет длиться и что из-за этого случится».
Три стратегии свежести данных
Первый вариант — SSR. Next.js генерирует страницу при каждом запросе. Данные всегда актуальные, но кэш не живёт дольше CDN-TTL. На карточке товара мы получали 1.1–1.4 секунды TTFB против 180 мс с ISR. При 28 000 SKU и высоком трафике это заметная нагрузка на Bitrix API.
Второй — ISR с TTL. Next.js строит статическую страницу и перестраивает её раз в N секунд, в фоне. Данные могут отставать на TTL. Мы ставили revalidate: 3600 — и именно это привело к ситуации с ценой. Снизить до 60 секунд? Тогда Next.js перестраивает 28 000 страниц 1440 раз в день. Это нагрузка, которую нужно считать заранее.
Третий — on-demand revalidation. Next.js инвалидирует конкретную страницу или тег по запросу извне — через revalidatePath() или revalidateTag(). Bitrix отправляет вебхук при изменении товара, Next.js принимает его через /api/revalidate и сбрасывает кэш только для затронутых страниц.
Это ближе всего к правильному решению. Но у него есть подводные камни.
Почему вебхук из Bitrix — это не серебряная пуля
Bitrix умеет отправлять вебхуки для части событий: изменение цены, изменение остатков, изменение описания товара. Но не для всех. Например, массовый импорт через 1C не всегда триггерит вебхук на каждый элемент — зависит от версии Bitrix и настроек обменника.
Вторая проблема — надёжность. Если Next.js недоступен в момент отправки вебхука, Bitrix не гарантирует повтор. Вебхук ушёл, никто не принял, инвалидации не произошло. Данные протухли — без какого-либо сигнала об этом.
Мы решили это через очередь. Bitrix пишет событие изменения в таблицу, cron читает её раз в минуту и дёргает /api/revalidate. Некрасиво. Зато надёжно.
Наш выбор: ISR + on-demand revalidation
В итоге у нас получилась гибридная стратегия:
- Все страницы каталога и карточек товаров: ISR с TTL 3600 секунд как fallback.
- On-demand revalidation через очередь событий — для цены и остатков (критичные для бизнеса поля).
- SSR — только для страниц корзины и личного кабинета, где данные должны быть актуальными всегда.
/api/revalidate принимает POST с токеном и списком slug'ов или тегов. Внутри — revalidateTag() для группы страниц или revalidatePath() для конкретного URL. Ничего сложного, но требует аккуратного мэппинга товар → страницы, которые его показывают (включая страницы категорий, если там есть плитки с ценой).
Что делать, если событие Bitrix не поддерживает вебхук
Несколько сценариев, с которыми мы столкнулись:
- Массовый импорт через 1C: не триггерит вебхук поштучно. Решение — после завершения обмена запускаем скрипт, который собирает изменённые элементы из лога обменника и ставит их в очередь на инвалидацию.
- Изменение через admin-интерфейс без сохранения через API: Bitrix пишет в базу напрямую, вебхук не генерируется. Решение — периодический диффинг (раз в 15 минут) ключевых полей через SQL-запрос к Bitrix БД.
- Изменение SEO-полей (meta title, description): не критично по времени, TTL 3600 достаточен.
Некоторые из этих решений выглядят как костыли. Они и есть костыли. Но они работают — и это честно.
Цена свежести: LCP и инфраструктура
Если перейти с ISR на SSR для всего каталога, TTFB вырастает с 180 мс до 1.1–1.4 с. LCP ухудшается примерно на 800–900 мс. Это прямое влияние на Core Web Vitals и, косвенно, на SEO.
On-demand revalidation с очередью добавляет latency в 1–2 минуты между изменением в Bitrix и обновлением на фронте. Для большинства товаров это приемлемо. Для акционных страниц с таймером — нет. Там мы поставили SSR.
Дополнительная нагрузка на Bitrix API при частой инвалидации — реальная. Мы отдельно тестировали: 50 одновременных revalidation-запросов при активном трафике. Bitrix держал, но следить за этим нужно.
Когда SSR честнее, чем ISR
ISR хорош для контента, который меняется редко и не требует мгновенной актуальности: статьи, категории, статичные страницы. Для цены в интернет-магазине — сложнее.
Если у вас есть юридические требования к актуальности цены (прайс-лист, защита прав потребителей), ISR с TTL 3600 — это риск. Прежде чем выбирать стратегию кэширования, проверьте с заказчиком: что будет, если покупатель увидит старую цену? Штраф? Конфликт? Просто извинение? Ответ на этот вопрос определяет архитектуру.
Кэш-инвалидация в headless — не техническая задача. Это бизнес-решение, которое нужно принять до запуска. Мы приняли его на третий день после. В следующий раз — это первый вопрос на предпроектном звонке.
Ещё по теме: