Ваш скрипт аналитики, скорее всего, отключает 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…