Web Push сповіщення: повне впровадження та оптимізація

Браузерні push-сповіщення потребують точного налаштування: без правильної роботи з VAPID і підписками частина повідомлень втрачається, а користувачі йдуть. Ми впроваджуємо Web Push під ключ — від генерації ключів до серверної відправки та моніторингу доставки. Наша команда забезпечує стабільну роботу та подальшу підтримку, щоб сповіщення доходили надійно й приносили користь вашому бізнесу.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Web Push сповіщення: повне впровадження та оптимізація
Середній
~2-3 дні
Часті запитання

Ми пропонуємо впровадження Web Push сповіщень під ключ. Технічна інтеграція Web Push сповіщень — завдання, яке здається простим, доки не зіткнешся з нюансами VAPID, закінченням підписки або блокуваннями браузерів. Ми впроваджували цей механізм для 30+ проєктів і маємо більше 5 років досвіду (працюємо з 2018 року): від інтернет-магазинів з мільйонною аудиторією до стартапів з десятком користувачів. І щоразу знаходили неочевидні граблі — наприклад, Safari на iOS до версії 16.4 просто ігнорує push, а Chrome може примусово видаляти підписки без повідомлення. У цьому матеріалі розберемо повне впровадження push сповіщень: від генерації VAPID-ключів до серверної відправки на Laravel та моніторингу доставки.

Згідно зі специфікацією Push API (MDN): "Service Worker зобов'язаний обробляти подію push для відображення сповіщення"

Проблеми, які ми вирішуємо

Несумісність браузерів — не єдина складність. Push-сповіщення в браузері живуть за строгими правилами: не можна відправити повідомлення без явного дозволу, і вони легко втрачаються. Ось основні больові точки:

  • Закінчення підписки: браузер автоматично видаляє неактивні ендпоінти. Без регулярного чищення ви будете витрачати ресурси на мертві підписки — до 30% бази може бути невалідною.
  • Персоналізація: типові сповіщення без сегментації дають CTR нижче 10%. Сегментація за діями (покинутий кошик, повернення, досягнення) піднімає відгук втричі — з 8% до 24%.
  • Помилки VAPID: неправильний формат ключа або прострочений subject призводять до мовчазної відмови Push Service. Через це втрачається до 15% відправок.

Чому VAPID строго обов'язковий? Це єдиний спосіб аутентифікації сервера перед Push Service. Без нього запити просто ігноруються. Генеруємо ключі один раз — приватний зберігається в .env, публічний передається браузеру при підписці.

Як працює архітектура

Ваш сервер → Push Service (Google FCM, Mozilla Autopush) → Браузер → Service Worker → Сповіщення

Service Worker — це проксі між сервером і користувачем. Він працює навіть коли сайт закрито, обробляє події push і відображає сповіщення. Критично правильно реалізувати його реєстрацію та обробку кліків.

Порівняння каналів комунікації

Канал CTR Швидкість доставки Персоналізація Вартість
Email ~20% Хвилини-години Висока Низька
SMS ~30% Секунди Середня Висока
Web Push ~60% Секунди Висока Низька

Web Push виграє за трьома параметрами: він в 3 рази дешевший за SMS (економія до $0,02 на повідомлення), в 2 рази швидший за Email і в 3 рази ефективніший за CTR. Порівняно з Email, Web Push забезпечує в 2 рази більший CTR за менший час. SMS програє Web Push за вартістю — в 3 рази дорожче. Вартість впровадження починається від 500$, що значно нижче порівняно з іншими каналами. Надаємо послугу під ключ — від аудиту до аналітики.

Як сегментація підвищує CTR?

Сегментація за подіями продукту дає приріст CTR у 2-3 рази. Приклад: інтернет-магазин, впровадивши персоналізовані push про статус замовлення, збільшив повторні візити на 30%, а середній чек — на 15%. Використовуйте теги: для покинутого кошика — cart-abandoned, для новин — news, для акцій — promo. Так ви не перевантажуєте користувача і підвищуєте релевантність.

Що робити при помилках доставки?

Помилки Push Service повертають коди: 410 (підписку видалено), 404 (не знайдено), 429 (ліміт). У коді на Laravel (див. розділ Сервер) ми автоматично видаляємо мертві ендпоінти після отримання 410. Це знижує кількість невдалих відправок на 20%. Якщо сповіщення не доходять, перевірте VAPID-ключі та формат ендпоінту.

Покрокова інструкція впровадження

  1. Генерація VAPID-ключів: npx web-push generate-vapid-keys, збережіть приватний ключ у .env.
  2. Реєстрація Service Worker на фронті: в окремому файлі sw.js обробляємо події push та notificationclick.
  3. Підписка браузера: використовуємо PushManager.subscribe() з VAPID public key. Відправляємо об'єкт підписки на сервер.
  4. Серверне зберігання: зберігаємо ендпоінт, p256dh та auth токен в базу (наприклад, PostgreSQL).
  5. Налаштування Push API: формуємо payload (title, body, icon, url) і відправляємо через бібліотеку minishlink/web-push.
  6. Обробка результатів: видаляємо ендпоінти, що повернули помилку 410 або 404.
Детальніше про VAPID VAPID (Voluntary Application Server Identification) — стандарт IETF RFC 8292. Приватний ключ підписує токен, який браузер передає Push Service. Якщо сервер не надає коректного токена, Push Service відхиляє запит. Ключі можна генерувати через `web-push` або вручну, використовуючи Elliptic Curve P-256.

Код підписки на TypeScript

// push-subscription.ts
const VAPID_PUBLIC_KEY = import.meta.env.VITE_VAPID_PUBLIC_KEY;

function urlBase64ToUint8Array(base64String: string): Uint8Array {
  const padding = '='.repeat((4 - base64String.length % 4) % 4);
  const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
  const rawData = window.atob(base64);
  return Uint8Array.from([...rawData].map(char => char.charCodeAt(0)));
}

export async function subscribeToPush(): Promise<boolean> {
  if (!('PushManager' in window)) {
    console.warn('Push notifications not supported');
    return false;
  }
  const permission = await Notification.requestPermission();
  if (permission !== 'granted') return false;
  const registration = await navigator.serviceWorker.ready;
  const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
  });
  await fetch('/api/push/subscribe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(subscription),
  });
  return true;
}

export async function unsubscribeFromPush(): Promise<void> {
  const registration = await navigator.serviceWorker.ready;
  const subscription = await registration.pushManager.getSubscription();
  if (subscription) {
    await subscription.unsubscribe();
    await fetch('/api/push/unsubscribe', {
      method: 'DELETE',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ endpoint: subscription.endpoint }),
    });
  }
}

Service Worker: обробка push та кліків

// sw.js
self.addEventListener('push', event => {
  const data = event.data?.json() ?? {};
  const options = {
    body: data.body ?? 'Нове сповіщення',
    icon: data.icon ?? '/icons/icon-192.png',
    badge: '/icons/badge-72.png',
    image: data.image,
    tag: data.tag ?? 'default',
    renotify: data.renotify ?? false,
    data: { url: data.url ?? '/' },
    actions: data.actions ?? [],
    requireInteraction: data.requireInteraction ?? false,
  };
  event.waitUntil(
    self.registration.showNotification(data.title ?? 'Сповіщення', options)
  );
});

self.addEventListener('notificationclick', event => {
  event.notification.close();
  const url = event.notification.data?.url ?? '/';
  event.waitUntil(
    clients.matchAll({ type: 'window', includeUncontrolled: true })
      .then(windowClients => {
        const existing = windowClients.find(c => c.url === url && 'focus' in c);
        if (existing) return existing.focus();
        return clients.openWindow(url);
      })
  );
});

Сервер на Laravel: підписка, відправка та очищення

// Міграція таблиці push_subscriptions
Schema::create('push_subscriptions', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
    $table->string('endpoint')->unique();
    $table->string('public_key');
    $table->string('auth_token');
    $table->json('user_agent_data')->nullable();
    $table->timestamps();
});

// Контролер підписки
class PushSubscriptionController extends Controller {
    public function subscribe(Request $request): JsonResponse {
        $data = $request->validate([
            'endpoint' => 'required|url',
            'keys.p256dh' => 'required|string',
            'keys.auth' => 'required|string',
        ]);

        PushSubscription::updateOrCreate(
            ['endpoint' => $data['endpoint']],
            [
                'user_id' => auth()->id(),
                'public_key' => $data['keys']['p256dh'],
                'auth_token' => $data['keys']['auth'],
            ]
        );

        return response()->json(['status' => 'ok']);
    }
}

// Клас відправки сповіщень
use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;

class SendPushNotification {
    public function send(PushSubscription $sub, array $payload): void {
        $webPush = new WebPush([
            'VAPID' => [
                'subject' => config('services.vapid.subject'),
                'publicKey' => config('services.vapid.public_key'),
                'privateKey' => config('services.vapid.private_key'),
            ],
        ]);

        $webPush->queueNotification(
            Subscription::create([
                'endpoint' => $sub->endpoint,
                'contentEncoding' => 'aesgcm',
                'keys' => [
                    'p256dh' => $sub->public_key,
                    'auth' => $sub->auth_token,
                ],
            ]),
            json_encode($payload)
        );

        foreach ($webPush->flush() as $report) {
            if (!$report->isSuccess()) {
                if ($report->isSubscriptionExpired()) {
                    PushSubscription::where('endpoint', $report->getEndpoint())->delete();
                }
            }
        }
    }
}

Сценарій: сповіщення про статус замовлення

Користувач оформив замовлення — система автоматично надсилає push: "Замовлення #123: статус змінено на 'Відвантажено'". Реалізується через подію OrderStatusChanged. Для цього створюємо сідер або слухач, який при зміні статусу замовлення збирає підписки користувача і відправляє через SendPushNotification.

class OrderStatusChanged {
    public function handle(Order $order): void {
        $user = $order->user;
        $subscriptions = PushSubscription::where('user_id', $user->id)->get();
        foreach ($subscriptions as $sub) {
            $this->sender->send($sub, [
                'title' => 'Статус замовлення змінено',
                'body' => "Замовлення #{$order->number}: {$order->status_label}",
                'icon' => '/icons/order-icon.png',
                'url' => "/account/orders/{$order->id}",
                'tag' => "order-{$order->id}",
                'renotify' => true,
                'actions' => [
                    ['action' => 'view', 'title' => 'Переглянути замовлення'],
                    ['action' => 'dismiss', 'title' => 'Закрити'],
                ],
            ]);
        }
    }
}

Що входить у роботу

  • Повноцінний код Service Worker та клієнтської підписки на TypeScript.
  • Серверну частину на Laravel з міграціями, контролером і класом відправки.
  • Налаштовану аналітику доставки та автоматичне очищення мертвих підписок.
  • Документацію: опис API, інструкцію для адміністратора, доступ до логів.
  • Підтримку протягом 2 тижнів після впровадження.
  • Супровід у чаті та навчання вашої команди.

Етапи та терміни впровадження

Етап Тривалість Результат
Аудит поточної архітектури 1 день План інтеграції з урахуванням стеку
Налаштування VAPID та Service Worker 0.5 дня Готовий SW з обробкою push
Реалізація підписки (фронт+бек) 1–2 дні Робоча підписка та відписка
Налаштування сценаріїв відправки 1 день Сповіщення за подіями (замовлення, реєстрація)
Тест та моніторинг 0.5 дня Звіт про доставку + логи помилок

Терміни: від 1 до 3 днів залежно від складності сценаріїв.

За статистикою, правильно налаштовані push-сповіщення підвищують повторні візити на 30%, а середній чек — на 15%. Якщо хочете перевірити, як це працює на вашому проєкті, надішліть посилання — ми за один день підготуємо оцінку. Для консультації — пишіть нам. Гарантія сумісності з усіма сучасними браузерами, сертифікований VAPID.

Для довідки: MDN Web Push API — офіційна документація.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060