Веб Push: полное руководство по реализации уведомлений в браузере

Быстрые ссылки
- Сервис-воркер
- Регистрация подписки на push
- Когда запрашивать подписку у пользователя
- Обработка истечений и обновлений подписок
- Добавление кнопок действий в уведомления
- Чек-лист внедрения
- План отладки и отката
- Резюме
Введение
Push-уведомления стали стандартной возможностью современных веб-приложений и PWA. Они дают способ донести важную информацию пользователю независимо от того, открыт ли сайт сейчас. Браузер получает push-событие и вызывает сервис-воркер, который затем отображает уведомление через интерфейсы ОС (центр уведомлений Windows, экран блокировки Android и т.п.).
Реализация Web Push на сайте требует работы с двумя отдельными компонентами:
- Клиентская часть в сервис-воркере — она принимает push-события и отображает уведомления с помощью Notification API.
- Серверная часть — она хранит подписки (endpoints и ключи) и отправляет зашифрованные push-сообщения через платформу доставки браузера.
Ниже — подробное, практическое руководство с примерами, проверками приёмки и планом на случай ошибок.
Сервис-воркер
Сервис-воркер — это фоновый скрипт в браузере. Он реагирует на события: fetch, sync, push и другие. Для Web Push ключевое событие — push. Оно предоставляет объект PushEvent, через который доступен полезный нагруз (payload).
Пример минимальной обработки push в сервис-воркере:
self.addEventListener("push", e => {
const payload = e.data ? JSON.parse(e.data.text()) : {title: "", body: ""};
e.waitUntil(
self.registration.showNotification(
payload.title,
{
body: payload.body,
icon: "/icon.png"
}
)
);
});Ключевые моменты:
- showNotification() вызывается на self.registration.
- Обёртка в e.waitUntil() гарантирует, что браузер не завершит работу воркера до отображения уведомления.
- Сервер обычно отправляет JSON в поле данных push-сообщения. Код должен корректно обрабатывать отсутствие данных.
Дополнительно — регистрация сервис-воркера на стороне главного скрипта:
if (navigator.serviceWorker) {
// Замените путь на ваш файл сервис-воркера
navigator.serviceWorker.register('/sw.js').catch(() => {
console.error("Не удалось зарегистрировать сервис-воркер.");
});
}Запускайте регистрацию на каждой загрузке страницы. Браузеры будут автоматически обновлять сервис-воркер, если файл на сервере изменился.
Регистрация подписки на push
Подписку на push выполняют в основном коде приложения (не в сервис-воркере). Процесс выглядит так:
- Получить регистрацию сервис-воркера.
- Проверить наличие текущей подписки через pushManager.getSubscription().
- Если подписки нет — запросить публичный VAPID-ключ с сервера и вызвать pushManager.subscribe({applicationServerKey, userVisibleOnly: true}).
- Отправить данные подписки на сервер, связав их с пользователем/устройством.
Пример функции подписки:
async function subscribeToPush() {
if (!navigator.serviceWorker) return;
const reg = await navigator.serviceWorker.getRegistration();
if (!reg || !reg.pushManager) return;
const subscription = await reg.pushManager.getSubscription();
if (!subscription) {
const key = await fetch('https://example.com/vapid_key');
const keyData = await key.text();
const sub = await reg.pushManager.subscribe({
applicationServerKey: keyData,
userVisibleOnly: true
});
await fetch('https://example.com/push_subscribe', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
endpoint: sub.endpoint,
expirationTime: sub.expirationTime,
keys: sub.toJSON().keys
})
});
}
}
// вызов
await subscribeToPush();Пояснения:
- applicationServerKey — это публичный ключ VAPID (URL-safe base64) в формате, который принимает браузер.
- userVisibleOnly: true указывает браузеру, что все push-сообщения будут отображать уведомление пользователю.
- PushSubscription.toJSON() возвращает keys с полями p256dh и auth, которые нужны серверу для шифрования и подписи.
Пример серверного потока отправки уведомлений
На сервере последовательность обычно такова:
- Получить подписки (endpoints + keys) для пользователя/группы.
- Сформировать и подписать VAPID-заголовки.
- Отправить зашифрованный payload на endpoint каждой подписки (через fetch или HTTP-клиент).
- Обработать ошибки (410 Gone — обычно значит, что подписка удалена) и удалить такие записи из БД.
Не используйте необработанные ответы от доставщика уведомлений: всегда логируйте коды ошибок и корректно удаляйте устаревшие endpoints.
Когда запрашивать разрешение на подписку
Запрос разрешения на отправку уведомлений вызывает браузерное диалоговое окно. Моменты, когда стоит предлагать подписку:
- После прямого пользовательского действия: клик по кнопке “Включить уведомления”.
- После объяснения ценности через баннер или модальное окно: опишите, что именно пользователь получит.
- Не показывайте системный диалог сразу на входе — часто это приведёт к отказу.
Пример UX-баннера: сначала показывайте кастомный баннер с кнопкой “Включить уведомления”. При клике вызывайте подписку.

Обязательно предоставьте в приложении возможность отписаться. Пользователь может отозвать разрешение через настройки браузера, но удобная внутренняя кнопка для отписки улучшит доверие.
Пример отписки:
async function unsubscribePush() {
const reg = await navigator.serviceWorker.getRegistration();
const subscription = await reg.pushManager.getSubscription();
if (subscription) {
await subscription.unsubscribe();
await fetch(`https://example.com/push_unsubscribe/${encodeURIComponent(subscription.endpoint)}`, {method: 'DELETE'});
} else {
// уже отписан
}
}Обработка истечения и обновлений подписки
Объект PushSubscription может содержать поле expirationTime. На практике современные браузеры часто оставляют это поле null. Тем не менее, подписки могут меняться из-за обновлений платформы доставки.
Событие pushsubscriptionchange позволяет реагировать на изменение подписки. Есть две версии события: v1 (старое поведение) и v2 (новое). В старом варианте браузер удаляет подписку и не даёт прямого доступа к старому endpoint. В v2 событие содержит oldSubscription и newSubscription, что упрощает обновление серверной записи.
Рекомендуется реализовать обработчик для нового API и также иметь запасной механизм, если браузер поддерживает только старую версию — например, хранить текущий endpoint в IndexedDB или localStorage.
Пример реализации для v2:
self.addEventListener('pushsubscriptionchange', e => {
e.waitUntil((async () => {
// e.newSubscription и e.oldSubscription могут быть null в зависимости от реализации
if (e.newSubscription) {
await fetch('https://example.com/push_change', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
auth: (e.newSubscription.toJSON().keys?.auth || null),
endpoint: e.newSubscription.endpoint,
endpointOld: e.oldSubscription ? e.oldSubscription.endpoint : null,
expirationTime: e.newSubscription.expirationTime,
p256dh: (e.newSubscription.toJSON().keys?.p256dh || null)
})
});
} else {
// fallback: попробовать заново подписаться или уведомить сервер, что подписка пропала
}
})());
});Если хотите поддержать старое поведение, отслеживайте endpoint в IndexedDB и используйте его внутри обработчика, чтобы отправить серверу старый URL.
Компоненты уведомления и кнопки действий
Notification API позволяет задавать заголовок, тело, иконку, бейдж, паттерн вибрации и кнопки действий (actions). Каждое действие получает поле action, которое может быть строкой или URI — оно передаётся в событии notificationclick.
Пример уведомления с кнопкой:
self.registration.showNotification(
"Notification with actions",
{
body: "This notification has a button.",
actions: [
{
action: "/home",
title: "Go to Homescreen",
icon: "/home.png"
}
]
}
);Обработка клика по уведомлению и по кнопке:
self.addEventListener('notificationclick', e => {
const action = e.action; // строка, которую вы указали в actions
const notification = e.notification;
notification.close();
// Открыть окно или фокусировать уже открытый клиент
e.waitUntil(clients.matchAll({type: 'window'}).then(windowClients => {
for (let client of windowClients) {
if (client.url === `${self.location.origin}${action}` && 'focus' in client) {
return client.focus();
}
}
if (clients.openWindow) {
return clients.openWindow(`${self.location.origin}${action}`);
}
}));
});Обратите внимание:
- Всегда вызывайте notification.close() при обработке клика, чтобы уведомление не висело в UI.
- На разных платформах действия могут выглядеть и работать по-разному. Тестируйте на целевых ОС.

Чек-лист внедрения (роль: разработчик фронтенда)
- Зарегистрирован и корректно обновляется сервис-воркер.
- Реализован обработчик push в сервис-воркере с e.waitUntil().
- Реализован вызов showNotification() с полезной структурой payload.
- Реализована подписка pushManager.subscribe() и отправка подписки на сервер.
- На сервере хранятся endpoint, p256dh, auth и привязка к пользователю/устройству.
- Обработаны ошибки отправки на сервере (410/404/403) и удаление устаревших подписок.
- Реализована отписка на клиенте и удаление записи на сервере.
- Добавлен UX для запроса разрешений (перед показом системного диалога показывать объясняющий баннер).
- Тестирование на целевых браузерах и ОС (Chrome Desktop/Android, Firefox, Edge; Safari — частично поддерживает/не поддерживает в зависимости от версии).
- Реализован pushsubscriptionchange и резервное хранение endpoint.
- Включены механизмы логирования и мониторинга отправки push-сообщений.
План отладки, ошибки и откат
- Локальная проверка сервис-воркера: откройте DevTools -> Application -> Service Workers. Убедитесь, что sw.js зарегистрирован и нет ошибок.
- Проверка подписки: в консоли выполните navigator.serviceWorker.getRegistration().then(r=>r.pushManager.getSubscription()).then(console.log).
- Логи сервера: проверяйте ответы от поставщика push (статусы 201/200/410/404 и тела ошибок).
- Ошибки отправки: 410 Gone — удаляйте подписку; 403/401 — проверьте VAPID; 400 — проверьте формат и шифрование payload.
- Откат: если новая версия сервис-воркера вызывает регрессии, фиксируйте версию и возвращайте предыдущий sw.js в CDN/серверном релизе. Браузер обновит сервис-воркер при следующей перезагрузке.
Критические шаги при инциденте:
- Отключить массовую отправку уведомлений, чтобы не посылать вредоносный или спам-контент.
- Проанализировать логи доставщика уведомлений.
- Уведомить команду поддержки и при необходимости пользователей (через email или сайт).
Безопасность и приватность
- VAPID — это механизм аутентификации отправителя. Храните приватный ключ VAPID в защищённом месте (на сервере), не в клиентском коде.
- Payload обычно шифруется с использованием ключей, переданных браузером (p256dh и auth). Используйте проверенные библиотеки для шифрования Web Push (например, web-push для Node.js).
- Сохраняйте минимум пользовательских данных в подписке. Связывайте endpoint с внутренним идентификатором устройства/пользователя, а не с PII напрямую.
- Подумайте о GDPR/локальной регуляции: явное согласие пользователя и возможность полного удаления подписки и связанных данных.
Приватность: push-уведомления могут раскрывать содержимое сообщения в системных местах. Не отправляйте чувствительную информацию в уведомлениях (например, номера карт, пароли, приватные сообщения) без дополнительной защиты.
Совместимость и особенности разных браузеров
- Chrome (Desktop/Android) — активно поддерживает Web Push, полностью реализует VAPID и шифрование.
- Firefox — поддерживает Web Push; исторически использовал собственный механизм доставки, но API поддерживается.
- Edge (Chromium) — поведение похоже на Chrome.
- Safari — долгое время не поддерживал стандартный Web Push; новые версии Safari и iOS постепенно добавляют поддержку, но стоит проверять актуальность и особенности платформы.
Тестируйте на реальных устройствах и версиях ОС, так как внешний вид и поведение уведомлений (иконки, действия, приоритет) сильно зависят от системы.
Дополнительные подходы и альтернативы
- Использовать сторонние сервисы (Firebase Cloud Messaging, OneSignal и т. п.) для упрощённого управления подписками и аналитики. Это снимает часть работы по шифрованию и VAPID, но добавляет внешнюю зависимость.
- Для критичных уведомлений — реализовать дополнительную доставку через email или SMS как резервный канал.
Когда Web Push не подходит:
- Если сообщение требует высокой безопасности и конфиденциальности, лучше использовать защищённые каналы внутри приложения, а не push.
- Если большая часть пользовательской базы не согласится на разрешения, вложите усилия в улучшение UX предложения подписки или предложите альтернативные каналы.
Примеры тестовых сценариев и критерии приёмки
Критерии приёмки для релиза функционала Web Push:
- Подписка: пользователь может подписаться и сервер получает endpoint + ключи.
- Получение: подписанный пользователь получает уведомление при отправке тестового payload.
- Отображение: текст уведомления и иконка отображаются корректно на целевых платформах.
- Кнопки действий: нажатие на действие открывает ожидаемый URL или фокусирует окно приложения.
- Отписка: пользователь может отписаться, и сервер прекращает отправку уведомлений этому endpoint.
- Обработка 410: сервер удаляет устаревшие endpoints при получении статуса 410.
Тест-кейсы:
- Подписать новый пользовательский агент и отправить уведомление с пустым payload.
- Отправить уведомление с кнопками действий и проверить notificationclick.
- Принудительно вызвать unsubscribe и проверить, что push событие больше не доставляется.
- Смоделировать ответ поставщика 410 и убедиться, что запись удаляется на сервере.
Модели зрелости (Maturity levels)
- Уровень 0 — без уведомлений.
- Уровень 1 — базовая подписка и отправка уведомлений; нет удобного UX для подписки и отписки.
- Уровень 2 — UX-баннеры, обработка ошибок на сервере, логирование доставок и ошибок.
- Уровень 3 — аналитика доставок и открытий, сегментация подписок, A/B-тесты для сообщений.
- Уровень 4 — персонализация, динамическое формирование payload, резервные каналы доставки.
Планируйте эволюцию от базовой реализации к более зрелым уровням по приоритету для бизнеса.
Решающее дерево для принятия решения о включении Web Push (Mermaid)
flowchart TD
A[Есть важные срочные сообщения?] -->|Да| B[Пользователи готовы принимать push?]
A -->|Нет| C[Отложить push]
B -->|Да| D[Внедряем Web Push]
B -->|Не уверены| E[Добавить баннер, провести тест]
E -->|Положительный результат| D
E -->|Отрицательный| CТиповые ошибки и как их исправить
- Ошибка: pushManager отсутствует. Причина: сервис-воркер не зарегистрирован или браузер не поддерживает Web Push. Проверка: navigator.serviceWorker и Service Worker registration.
- Ошибка: 403 при отправке push с сервера. Причина: неправильные VAPID-ключи или плохая подпись. Решение: проверьте приватный ключ VAPID и библиотеку отправки.
- Ошибка: уведомления приходят, но без данных. Причина: сервер не прикрепил payload или браузер-бэкэнд удалил payload из-за ограничений. Решение: убедитесь, что вы отправляете зашифрованный payload корректно и обрабатываете null e.data на стороне воркера.
- Ошибка: notificationclick не срабатывает на некоторых платформах. Причина: отличия в реализации. Решение: тестируйте на целевых устройствах и реализуйте fallback (например, открывать страницу при клике на тело уведомления).
Шаблон политики уведомлений для продукта (короткая версия)
- Типы уведомлений: критические, информационные, маркетинговые.
- Критические уведомления отправлять всем подписанным пользователям; маркетинговые — только тем, кто явно согласился.
- Частота: не более N маркетинговых уведомлений в неделю (зависит от продукта).
- Уникальность контента: избегать дублирования и спама.
- Приватность: не включать PII в payload.
Короткое руководство для команды поддержки
- Проверить, подписан ли пользователь: попросить его открыть сайт, открыть DevTools или отправить снимок экрана блокировки/центра уведомлений.
- Если пользователь не получает уведомления: проверить, не отключены ли уведомления в настройках ОС/браузера.
- Для массовых проблем: проверить статус отправщика push (утечки ключей, изменение VAPID).
1-строчная глоссарий
- Service Worker — фоновой JavaScript-скрипт, реагирующий на события.
- Push API / PushManager — API для управления подписками на push.
- PushSubscription — объект, описывающий подписку (endpoint + ключи).
- VAPID — способ аутентификации отправителя уведомлений.
Резюме
Web Push — мощный инструмент для связи с пользователями. Его внедрение требует синхронной работы клиентской логики (сервис-воркер, Notification API) и серверной части (хранение подписок, VAPID, шифрование payload). Фокусируйтесь не только на технической реализации, но и на UX: объясняйте пользователю ценность уведомлений, давайте простой путь для отписки и соблюдайте правила приватности.
Важно протестировать поведение на целевых браузерах и платформах. Добавьте мониторинг доставки и корректную обработку ошибок на сервере. Маленькие улучшения UX (объясняющий баннер, индикаторы загрузки при подписке) заметно увеличат конверсию подписок.

Важно: поведение уведомлений и поддержка опций (иконки, действия, вибрация) различаются между платформами. Всегда проверяйте критические сценарии на реальных устройствах.
Похожие материалы
Spotify не может воспроизвести трек — способы исправить
Установка Google Play на Windows 11
AirDrop на Apple: настройка и безопасность
Уведомления об истечении пароля в Active Directory
Задержка в Routines Google Assistant