Если после релиза сайт продолжает показывать старую версию, сначала проверьте регистрацию Service Worker. Состояние waiting означает, что новый worker уже установлен, но старый ещё управляет открытыми вкладками. Это штатная защита от одновременной работы двух версий, а не обязательно ошибка кэша.
Безопасный порядок такой: подтвердить установку новой версии, предупредить пользователя, сохранить несохранённые данные, разрешить активацию и перезагрузить страницу после события controllerchange. Простого обновления вкладки часто недостаточно.
Почему новая версия остаётся в waiting
Браузер устанавливает обновлённый Service Worker рядом с активным. По умолчанию он ждёт, пока старый перестанет контролировать все страницы в своей области действия. Открытая вкладка, отдельное окно или установленное PWA могут удерживать прежнюю версию.
Разделяйте три ситуации:
waiting: новый worker установлен и ждёт активации;installing: установка ещё идёт или завершилась ошибкой;- обновление не найдено: браузер не увидел изменений либо проверяет не тот URL или scope.
Диагностика по шагам
1. Убедитесь, что страницу контролирует Service Worker
В Chrome или Edge откройте DevTools → Application → Service Workers. Сверьте адрес скрипта и область действия с проблемной страницей. В консоли проверьте текущий контроллер:
navigator.serviceWorker.controller?.scriptURL
Если результата нет, страница не находится под управлением worker. Проверьте scope регистрации, HTTPS и путь скрипта, а не очищайте кэш наугад.
2. Посмотрите состояния регистрации
const reg = await navigator.serviceWorker.getRegistration();
console.table({
installing: reg?.installing?.state,
waiting: reg?.waiting?.state,
active: reg?.active?.state
});
waiting: "installed" подтверждает, что новый скрипт доставлен и установлен. Если есть только active, выполните await reg.update() и снова проверьте состояния.
Если worker перешёл в redundant или не дошёл до installed, изучите ошибки установки. Причиной может быть синтаксическая ошибка, недоступный импорт или отклонённое обещание внутри install. Неудачная загрузка одного обязательного файла способна сорвать заполнение precache.
3. Проверьте, что изменился сам worker
Обновление определяется по содержимому скрипта Service Worker и его импортов. Если релиз поменял ресурсы, а итоговый worker остался прежним, браузеру может быть нечего устанавливать.
Не создавайте новый URL worker для каждого релиза. Оставьте стабильный адрес регистрации, а версии ресурсов отражайте в precache-манифесте или именах файлов с хешем. Запрос worker должен возвращать JavaScript, а не редирект на HTML или серверную ошибку.
4. Найдите вкладку, которая удерживает старую версию
Закройте все страницы сайта, включая отдельные окна и установленное приложение, затем откройте сайт снова. Если обновление активировалось, lifecycle работает штатно, но для долго открытых страниц не хватает управляемого сценария.
Обычная перезагрузка не всегда освобождает старый client вовремя: новая навигация начинается, пока прежняя страница ещё существует. Поэтому совет «нажмите F5» не заменяет обработку waiting.
Как активировать обновление безопасно
Для сайта с формами и долгими сессиями покажите сообщение «Доступна новая версия». После согласия пользователя сохраните черновик или предупредите о несохранённых данных, затем отправьте waiting-worker команду:
reg.waiting?.postMessage({type: 'SKIP_WAITING'});
navigator.serviceWorker.addEventListener('controllerchange', () => {
window.location.reload();
});
В Service Worker добавьте обработчик:
self.addEventListener('message', event => {
if (event.data?.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
Подписку на controllerchange установите до отправки сообщения и защитите перезагрузку от повторного вызова. Сначала испытайте сценарий на тестовом окружении с двумя вкладками и несохранённой формой.
Когда немедленный skipWaiting опасен
Безусловный self.skipWaiting() ускоряет активацию, но новый worker может начать обслуживать страницу, загруженную со старым кодом. Для несовместимых маршрутов, API или ресурсов это риск частично сломанного интерфейса.
clients.claim() тоже не универсальное лекарство: он передаёт уже открытые страницы новому worker. Используйте его только при совместимости старой страницы с новой логикой запросов. Для ломающего релиза безопаснее показать prompt и выполнить контролируемую перезагрузку.
Проверьте кэши и границы очистки
После активации удаляйте устаревшие кэши в событии activate, но только принадлежащие приложению. Используйте собственный префикс и список допустимых версий. Удаление всех записей Cache Storage опасно, если на одном origin работают несколько приложений.
Не просите пользователей постоянно очищать данные сайта или удалять регистрацию. Это маскирует дефект обновления и не доказывает, что следующий релиз пройдёт корректно.
Сетевой HTTP-кэш проверяется отдельно: порядок описан в статье «HTTP-кэш через curl: ревалидация и устаревший ответ».
Чек-лист приёмки релиза
Перед выпуском проверьте:
- открытая старая вкладка обнаруживает новую версию;
- вторая вкладка не вызывает бесконечные перезагрузки;
- несохранённая форма не теряется без предупреждения;
- после согласия
waitingисчезает, срабатываетcontrollerchange, а интерфейс показывает ожидаемый идентификатор сборки; - старые и новые ресурсы доступны на время перехода;
- офлайн-режим и повторный запуск PWA используют согласованный набор файлов.
Внешняя проверка дополняет этот тест, но не заменяет его: Web-Puls может регулярно контролировать публичную доступность критичного URL, однако не видит lifecycle Service Worker в браузере конкретного пользователя.
Если старая версия воспроизводится после релиза, подготовьте публичный URL, время проверки, ожидаемый идентификатор сборки и безопасный скрин состояний installing/waiting/active. С этими данными можно отправить заявку на диагностику после релиза без cookies, токенов и приватных адресов.