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

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

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

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

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

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

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

Етапи розробки

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1358
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1251
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    957
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1188
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    947

Ми пропонуємо впровадження 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 — офіційна документація.

Коли додаток не відкривається без інтернету, користувач іде до конкурентів

PWA перетворює сайт на надійний додаток, який працює навіть в офлайні, надсилає push-повідомлення та встановлюється на головний екран. Розробка PWA додатків — це впровадження Service Worker, налаштування кеш-стратегій та інтеграція Web Push. На відміну від нативних додатків, вам не потрібні дві команди під iOS та Android — один код працює у всіх сучасних браузерах. Замовте аудит вашого проекту на PWA-сумісність — ми безкоштовно оцінимо потенціал та терміни.

Як Service Worker керує мережевими запитами?

Service Worker — це JavaScript-проксі між браузером та мережею. Працює в окремому потоці, перехоплює запити та вирішує, звідки їх віддавати: з кешу, з мережі або комбінацією. Розглянемо три базові стратегії на реальних сценаріях.

Стратегія кешування Використання Поведінка при офлайн
Cache First статика (CSS/JS з content hash) віддається з кешу, миттєво
Network First API, замовлення, новини спочатку мережа, при помилці — кеш
Stale While Revalidate контент соцмереж, стрічка сторінок одразу кеш, потім оновлення

Cache First застосовується для активів з хешем в імені — файл ніколи не зміниться, можна кешувати назавжди. Stale While Revalidate оптимальна для контенту, де допустима невелика затримка актуалізації. Workbox від Google автоматизує версіонування кешу та інвалідацію — без нього коректний Service Worker вимагатиме 300+ рядків коду з нетривіальними edge cases. Vite + vite-plugin-pwa генерує Service Worker з конфігу, включаючи precaching статики.

import { VitePWA } from 'vite-plugin-pwa';
export default {
  plugins: [
    VitePWA({
      registerType: 'autoUpdate',
      includeAssets: ['favicon.ico'],
      manifest: { /* name, icons, start_url, display */ },
      workbox: {
        globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
        runtimeCaching: [
          { urlPattern: /^https?:\/\/api\./,
            handler: 'NetworkFirst',
            options: { cacheName: 'api-cache' }
          }
        ]
      }
    })
  ]
};

Як офлайн-режим реалізується на практиці?

«Працює офлайн» для різних продуктів означає різне. Ось три типові сценарії.

  • Офлайн-читання (новинні сайти, документація): Service Worker кешує сторінки при першому візиті, стратегія Stale While Revalidate + Background Sync для синхронізації після відновлення з'єднання.
  • Офлайн-редагування (нотатки, завдання): IndexedDB зберігає локальні дані, Background Sync API ставить операції в чергу — браузер синхронізує сам, навіть якщо вкладка закрита. Обмеження: Background Sync підтримується тільки в Chromium.
  • Офлайн-форма: користувач натиснув «Надіслати» без інтернету — дані не втрачаються, а ставляться в чергу та надсилаються автоматично. Для медичних та страхових форм це критично.

Проблема, про яку часто забувають: конфлікти при синхронізації. Якщо користувач А редагував запис офлайн, а користувач Б змінив його онлайн — потрібна стратегія вирішення (last-write-wins, three-way merge або показ конфлікту користувачеві). Ми опрацьовуємо ці сценарії на етапі проектування.

Як працюють Web Push-повідомлення?

Web Push доставляє повідомлення через браузер + Push Service (FCM для Chrome/Edge, APNs для Safari). Користувач надає дозвіл → браузер підписується на Push Service → ви отримуєте endpoint та ключі → надсилаєте повідомлення → Push Service доставляє в браузер. Реалізація через бібліотеку web-push (Node.js) або аналог для вашого бекенду. VAPID-ключі генеруються один раз, підписка зберігається в базі даних.

На поточний момент iOS (починаючи з версії 16.4) підтримує Web Push тільки для встановлених PWA, Chrome/Firefox/Edge — повна підтримка без встановлення. Частота та релевантність повідомлень безпосередньо впливають на відтік підписників — A/B тестування часу надсилання та формулювань стандартна практика.

Чому варто обрати PWA замість нативних додатків?

Економія на розробці під дві платформи — до 60% бюджету. Один код, єдина бізнес-логіка, автоматичне оновлення без магазинів. Ми виконали понад 30 PWA-проектів для e-commerce, фінтеху та корпоративних систем — гарантуємо сумісність з останніми версіями браузерів та відмінні показники Core Web Vitals (LCP, CLS, INP). PWA збільшує конверсію в середньому на 36% (дані Google). Сертифіковані спеціалісти з досвідом 7+ років у веб-розробці.

Що входить у розробку PWA під ключ?

Етап Результат Термін
Аудит поточного додатку Звіт PWA‑score, рекомендації 1–2 дні
Проектування офлайн‑сценаріїв Документація, прототип 2–3 дні
Розробка Service Worker + маніфест Код, автоматичне тестування 5–10 днів
Інтеграція Web Push (опціонально) Бекенд‑ендпоінт, підписка 3–5 днів
Тестування на реальних пристроях Звіт, правки 3–5 днів
Деплой та документація Доступи, інструкція, гарантія 1 місяць 1–2 дні

App Shell архітектура та precaching критичних ресурсів при першому встановленні дають миттєве завантаження оболонки додатку навіть при повільному з'єднанні.

Процес роботи та терміни

  1. Аудит поточного додатку (Lighthouse PWA score, аналіз сценаріїв).
  2. Визначення цінних офлайн-сценаріїв.
  3. Налаштування Service Worker через Workbox та реалізація маніфесту.
  4. Інтеграція Web Push (якщо потрібно).
  5. Тестування на реальних пристроях — Chrome DevTools, Safari Web Inspector.
  6. Деплой, документація, навчання команди.

Орієнтовні терміни: базова PWA (маніфест + Service Worker + кеш статики) — 1–2 тижні поверх готового додатку; Web Push — 1–2 тижні; офлайн-редагування з IndexedDB та Background Sync — 3–6 тижнів залежно від складності даних.

Вартість розраховується індивідуально після аудиту. PWA-проект зазвичай обходиться в 2–3 рази дешевше нативного додатку під обидві платформи. Додаткову економію дає відсутність витрат на публікацію в App Store та Google Play. Зв'яжіться з нами для консультації — ми безкоштовно оцінимо ваш проект та запропонуємо оптимальну стратегію впровадження.

Посилання для поглибленого вивчення