Назад к блогу

Ваш скрипт аналитики, скорее всего, отключает back/forward cache

Back/forward cache делает навигацию по кнопке «назад» почти мгновенной, но один слушатель unload отключает его для всей страницы. Обычно виноваты трекинговые скрипты — и теперь CrUX измеряет ущерб.

Самая быстрая навигация в вебе — та, при которой вообще ничего не загружается. Back/forward cache (bfcache) обеспечивает именно это — а одна строка в скрипте вашего поставщика аналитики может выключить его для каждой страницы вашего сайта.

bfcache — это оптимизация браузера, которая сохраняет полный снимок страницы в памяти (включая кучу JavaScript), когда пользователь уходит со страницы. Нажмите кнопку «назад», и браузер восстановит этот снимок и возобновит выполнение, обеспечив почти мгновенную загрузку без сетевого запроса. По оценкам, навигация назад и вперёд составляет 10–20% всех навигаций, так что это не редкий частный случай.

Один слушатель дисквалифицирует всю страницу

Право на попадание в bfcache по своей природе хрупкое. Браузер не заморозит страницу, на которой зарегистрирован слушатель события unload, потому что unload означает, что страница ожидает своего уничтожения. На десктопе Chrome и Firefox делают любую страницу со слушателем unload непригодной для bfcache — без исключений, без частичного зачёта.

Слушатель не обязательно ваш. Сторонние скрипты, регистрирующие unload изнутри вашей страницы или даже внутри подфрейма, дисквалифицируют документ верхнего уровня. У Lighthouse есть отдельный аудит no-unload-listeners именно потому, что виновный код так часто оказывается тем, который автор сайта никогда не писал.

beforeunload в современных браузерах больше не дисквалифицирует, но он ненадёжен, и его всё равно лучше избегать, если только у пользователя нет несохранённых изменений.

Трекинговые скрипты — обычные виновники

Событие unload — классическое место для отправки финального beacon: сбросить сессию, отправить событие «страница закрыта», — поэтому скрипты поведенческого трекинга и рекламы постоянно к нему прибегают.

fbevents.js от Facebook регистрирует обработчик unload и присутствует, по данным HTTP Archive, примерно на 9% всех веб-страниц. Тег PayPal внедряет iframe, который добавляет событие unload, блокируя bfcache во многих сценариях оформления заказа. Скрипты в подфреймах вроде hCaptcha делали то же самое. Ни одно из этого не требует изменений в вашем собственном коде, чтобы начать вам стоить — достаточно того, что поставщик выкатит обновление.

CrUX теперь показывает вам счёт

Раньше это было невидимо в полевых данных. Начиная с датасета за март 2024 года, Chrome User Experience Report (CrUX) публикует разбивку navigation_types — включая долю визитов, обслуженных из back/forward cache, — так что вы можете увидеть, сколько реальных пользователей лишаются мгновенного пути.

Корреляция поразительна. Анализ CrUX выявил сильную статистическую связь (ρ=0.87) между высокой долей back_forward_cache и instant_lcp_density — долей загрузок с LCP менее 200 мс. После того как обновление Google в марте 2026 года повысило вес LCP, INP и CLS в ранжировании, самостоятельно нанесённая блокировка bfcache становится измеримым недостатком на 75-м перцентиле, а не погрешностью округления.

Как обнаружить и исправить

Откройте DevTools, перейдите в Application → Back/forward cache и нажмите Run Test. Chrome перечислит каждую причину блокировки, включая обработчики unload, добавленные третьими сторонами.

Чтобы обнаруживать восстановления из bfcache в собственном коде, никогда не используйте unload. Используйте pageshow и проверяйте persisted:

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // restored from bfcache — no fresh page load fired
  }
})

Чтобы помешать третьим сторонам вас дисквалифицировать, установите заголовок ответа, который полностью запрещает слушатели unload:

Permissions-Policy: unload=()

Это нейтрализует обработчики unload от любого скрипта — тегов поставщиков, расширений или вашего собственного устаревшего кода, — так что страница остаётся пригодной для bfcache независимо от того, что загружается.

Трекер, который к нему вообще не прикасается

Структурное решение — использовать инструментирование, у которого попросту нет причин слушать unload. Privacy-first трекер записывает просмотр страницы одним запросом keepalive и позволяет запросу пережить страницу самостоятельно — никакого финального beacon, никакого обработчика unload, ничего, что браузер мог бы пометить:

fetch('/collect', {
  method: 'POST',
  keepalive: true,
  body: JSON.stringify({ site_id, path: location.pathname })
})

Когда трекеру нужно отреагировать на то, что страница ушла в фон, правильный сигнал — visibilitychange или pagehide, и оба срабатывают, не делая страницу непригодной для bfcache. Трекер Monoid весит примерно 2 КБ, не ставит cookie и подключается к history.pushState для смены маршрутов в SPA, а не к жизненному циклу страницы, — так что ему нечего регистрировать через unload и нечем отключать ваш back/forward cache.

Слежечная аналитика облагает производительность налогом такими способами, которые не проявляются в лабораторном прогоне. Блокировка bfcache — одна из самых тихих: ни ошибки, ни медленного рендера, просто кнопка «назад», которая перезагружает из сети, когда должна была восстановить из памяти.

Источники

Comments

Loading comments…