Реализация Push-уведомлений с помощью Web Push API

Push-уведомления поддерживают вовлечённость пользователей даже когда они не используют ваше веб-приложение активно. Web Push API позволяет отправлять своевременные обновления напрямую на устройства пользователей без использования сторонних сервисов. Это руководство показывает, как реализовать нативные уведомления с использованием Service Workers, VAPID-аутентификации и правильного шифрования.
Ключевые моменты
- Web Push API обеспечивает нативные push-уведомления без сторонних сервисов
- Service Workers и VAPID-аутентификация являются основными компонентами
- HTTPS-соединение обязательно для реализации
- Разные браузеры имеют различные требования к push-уведомлениям
Предварительные требования и поддержка браузерами
Перед реализацией push-уведомлений убедитесь, что ваше приложение соответствует следующим требованиям:
- HTTPS-соединение (обязательно для Service Workers)
- Поддержка современных браузеров (Chrome, Firefox, Edge, Safari 16.4+)
- Совместимость с Service Worker
// Feature detection
if ('serviceWorker' in navigator && 'PushManager' in window) {
// Push notifications supported
}
Регистрация Service Worker
Service Worker выступает в качестве прокси между вашим веб-приложением и push-сервисами. Зарегистрируйте его при загрузке страницы:
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker registered:', registration);
})
.catch(error => {
console.error('Registration failed:', error);
});
Настройка VAPID-ключей
VAPID (Voluntary Application Server Identification) аутентифицирует ваш сервер в push-сервисах. Сгенерируйте пару ключей один раз для вашего приложения:
# Using web-push library (Node.js)
npm install web-push
npx web-push generate-vapid-keys
Храните приватный ключ в безопасности на вашем сервере и используйте публичный ключ в клиентской подписке.
Создание Push-подписок
Запросите разрешение и подпишите пользователей на push-уведомления:
async function subscribeToPush() {
const registration = await navigator.serviceWorker.ready;
// Request permission (only on user gesture)
const permission = await Notification.requestPermission();
if (permission !== 'granted') return;
// Subscribe with VAPID public key
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
// Send subscription to your server
await fetch('/api/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(subscription)
});
}
// Helper function to convert base64 to Uint8Array
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
Объект подписки содержит:
- Endpoint: Уникальный URL для отправки сообщений
- Keys: Ключи шифрования (p256dh и auth) для безопасности полезной нагрузки
Discover how at OpenReplay.com.
Серверная реализация
Ваш сервер должен шифровать сообщения и отправлять их на endpoint push-сервиса. Использование библиотеки web-push упрощает это:
const webpush = require('web-push');
webpush.setVapidDetails(
'mailto:your-email@example.com',
VAPID_PUBLIC_KEY,
VAPID_PRIVATE_KEY
);
// Send notification
const payload = JSON.stringify({
title: 'New Message',
body: 'You have a new update',
icon: '/icon-192.png',
url: 'https://example.com/updates'
});
webpush.sendNotification(subscription, payload, {
TTL: 86400, // 24 hours
urgency: 'high'
})
.catch(error => {
console.error('Error sending notification:', error);
});
Обработка Push-событий в Service Worker
Service Worker получает и отображает push-уведомления:
// sw.js
self.addEventListener('push', event => {
const data = event.data?.json() || {};
const options = {
body: data.body || 'Default message',
icon: data.icon || '/icon.png',
badge: '/badge.png',
vibrate: [200, 100, 200],
data: { url: data.url }
};
event.waitUntil(
self.registration.showNotification(data.title || 'Notification', options)
);
});
// Handle notification clicks
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data?.url || '/')
);
});
Требования для конкретных браузеров
Различные браузеры применяют разные требования для реализации Web Push API:
- Chrome/Edge: Требуют видимые уведомления для каждого push-сообщения
- Firefox: Позволяет ограниченные тихие push с системой квот
- Safari: Требует видимые уведомления и не поддерживает тихие push
Примечание: Размеры полезной нагрузки ограничены: Chrome/Edge/Firefox поддерживают до 4 КБ, в то время как Safari поддерживает 2 КБ. Сохраняйте сообщения лёгкими и загружайте дополнительные данные в приложении при необходимости.
Всегда отображайте уведомления немедленно при получении push-событий для сохранения разрешений во всех браузерах.
Рекомендации по безопасности
Защитите вашу реализацию push-уведомлений:
- Защищённые endpoints: Никогда не раскрывайте endpoints подписки публично
- Шифруйте полезную нагрузку: Все данные сообщений должны использовать ECDH-шифрование
- Защищайте VAPID-ключи: Храните их в безопасности и перегенерируйте только в случае компрометации
- Реализуйте CSRF-защиту: Валидируйте запросы на подписку
- Ограничение частоты запросов: Предотвращайте злоупотребления с помощью серверного троттлинга
Управление жизненным циклом подписки
Обрабатывайте изменения и истечение подписки:
// In Service Worker
self.addEventListener('pushsubscriptionchange', event => {
event.waitUntil(
self.registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
})
.then(subscription => {
// Update server with new subscription
return fetch('/api/update-subscription', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(subscription)
});
})
);
});
Заключение
Web Push API предоставляет стандартизированный подход к реализации push-уведомлений без привязки к конкретному поставщику. Комбинируя Service Workers, VAPID-аутентификацию и правильное шифрование, вы можете доставлять своевременные уведомления, которые работают в современных браузерах. Не забывайте уважать предпочтения пользователей, обрабатывать события жизненного цикла подписки и следовать платформенным требованиям для надёжной реализации.
Начните с базовой доставки уведомлений, затем добавляйте функции, такие как кнопки действий, вложения изображений и группировка уведомлений по мере развития вашей реализации.
Часто задаваемые вопросы
Нет, push-уведомления требуют активного подключения к интернету для получения сообщений от push-сервиса. Однако Service Workers могут кэшировать уведомления и отображать их при восстановлении соединения.
Когда разрешение отклонено, вы не можете отправлять push-уведомления этому пользователю. Вы должны дождаться, пока он вручную изменит разрешения в настройках браузера. Рассмотрите возможность реализации альтернативных методов вовлечения, таких как внутриприложенные уведомления.
Большинство push-сервисов ограничивают размер полезной нагрузки до 4 КБ. Chrome и Firefox поддерживают до 4 КБ, в то время как Safari поддерживает до 2 КБ. Сохраняйте полезную нагрузку минимальной и загружайте дополнительные данные при получении уведомления при необходимости.
Да, push-уведомления могут быть получены, когда браузер закрыт на большинстве платформ. Однако это зависит от операционной системы и браузера. Мобильные браузеры могут иметь ограничения, основанные на настройках оптимизации батареи.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.