Web Push APIを使用したプッシュ通知の実装
  プッシュ通知は、ユーザーがWebアプリケーションをアクティブに使用していない場合でも、エンゲージメントを維持します。Web Push APIを使用すると、サードパーティサービスに依存することなく、タイムリーな更新をユーザーのデバイスに直接送信できます。このガイドでは、Service Worker、VAPID認証、適切な暗号化を使用して、ネイティブアプリのような通知を実装する方法を説明します。
重要なポイント
- Web Push APIは、サードパーティサービスなしでネイティブプッシュ通知を実現
 - Service WorkerとVAPID認証が必須コンポーネント
 - 実装にはHTTPS接続が必須
 - ブラウザごとにプッシュ通知の要件が異なる
 
前提条件とブラウザサポート
プッシュ通知を実装する前に、アプリケーションが以下の要件を満たしていることを確認してください:
- HTTPS接続(Service Workerに必須)
 - モダンブラウザのサポート(Chrome、Firefox、Edge、Safari 16.4以降)
 - Service Workerの互換性
 
// 機能検出
if ('serviceWorker' in navigator && 'PushManager' in window) {
  // プッシュ通知がサポートされています
}
Service Workerの登録
Service Workerは、Webアプリとプッシュサービスとのプロキシとして機能します。ページ読み込み時に登録します:
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)は、プッシュサービスでサーバーを認証します。アプリケーション用に一度だけキーペアを生成します:
# web-pushライブラリを使用(Node.js)
npm install web-push
npx web-push generate-vapid-keys
秘密鍵はサーバー上で安全に保管し、公開鍵はクライアント側のサブスクリプションで使用します。
プッシュサブスクリプションの作成
ユーザーに許可を要求し、プッシュ通知にサブスクライブします:
async function subscribeToPush() {
  const registration = await navigator.serviceWorker.ready;
  
  // 許可をリクエスト(ユーザージェスチャー時のみ)
  const permission = await Notification.requestPermission();
  if (permission !== 'granted') return;
  
  // VAPID公開鍵でサブスクライブ
  const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
  });
  
  // サブスクリプションをサーバーに送信
  await fetch('/api/subscribe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(subscription)
  });
}
// base64を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;
}
サブスクリプションオブジェクトには以下が含まれます:
- エンドポイント: メッセージ送信用の一意のURL
 - キー: ペイロードのセキュリティのための暗号化キー(p256dhとauth)
 
Discover how at OpenReplay.com.
サーバー側の実装
サーバーはメッセージを暗号化し、プッシュサービスのエンドポイントに送信する必要があります。web-pushライブラリを使用すると、これが簡単になります:
const webpush = require('web-push');
webpush.setVapidDetails(
  'mailto:your-email@example.com',
  VAPID_PUBLIC_KEY,
  VAPID_PRIVATE_KEY
);
// 通知を送信
const payload = JSON.stringify({
  title: '新しいメッセージ',
  body: '新しい更新があります',
  icon: '/icon-192.png',
  url: 'https://example.com/updates'
});
webpush.sendNotification(subscription, payload, {
  TTL: 86400, // 24時間
  urgency: 'high'
})
  .catch(error => {
    console.error('Error sending notification:', error);
  });
Service WorkerでのPushイベントの処理
Service Workerはプッシュ通知を受信して表示します:
// sw.js
self.addEventListener('push', event => {
  const data = event.data?.json() || {};
  
  const options = {
    body: data.body || 'デフォルトメッセージ',
    icon: data.icon || '/icon.png',
    badge: '/badge.png',
    vibrate: [200, 100, 200],
    data: { url: data.url }
  };
  
  event.waitUntil(
    self.registration.showNotification(data.title || '通知', options)
  );
});
// 通知クリックの処理
self.addEventListener('notificationclick', event => {
  event.notification.close();
  
  event.waitUntil(
    clients.openWindow(event.notification.data?.url || '/')
  );
});
ブラウザ固有の要件
ブラウザによって、Web Push API実装の要件が異なります:
- Chrome/Edge: すべてのプッシュメッセージに対して可視通知が必要
 - Firefox: クォータシステムで制限付きのサイレントプッシュを許可
 - Safari: 可視通知が必要で、サイレントプッシュはサポートされていない
 
注意: ペイロードサイズには制限があります。Chrome/Edge/Firefoxは最大4KBをサポートし、Safariは2KBをサポートしています。メッセージは軽量に保ち、必要に応じてアプリ内で追加データを取得してください。
すべてのブラウザで許可を維持するため、プッシュイベントを受信したら必ず通知を即座に表示してください。
セキュリティのベストプラクティス
プッシュ通知の実装を保護します:
- エンドポイントの保護: サブスクリプションエンドポイントを公開しない
 - ペイロードの暗号化: すべてのメッセージデータはECDH暗号化を使用する必要がある
 - VAPIDキーの保護: 安全に保管し、侵害された場合のみ再生成する
 - CSRF保護の実装: サブスクリプションリクエストを検証する
 - レート制限: サーバー側のスロットリングで悪用を防止する
 
サブスクリプションライフサイクルの管理
サブスクリプションの変更と有効期限を処理します:
// Service Worker内
self.addEventListener('pushsubscriptionchange', event => {
  event.waitUntil(
    self.registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
    })
      .then(subscription => {
        // 新しいサブスクリプションでサーバーを更新
        return fetch('/api/update-subscription', {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(subscription)
        });
      })
  );
});
まとめ
Web Push APIは、ベンダーロックインなしでプッシュ通知を実装するための標準ベースのアプローチを提供します。Service Worker、VAPID認証、適切な暗号化を組み合わせることで、モダンブラウザで動作するタイムリーな通知を配信できます。ユーザーの設定を尊重し、サブスクリプションライフサイクルイベントを処理し、堅牢な実装のためにプラットフォーム固有の要件に従うことを忘れないでください。
基本的な通知配信から始めて、実装が成熟するにつれて、アクションボタン、画像添付、通知のグループ化などの機能を追加してください。
よくある質問
いいえ、プッシュ通知はプッシュサービスからメッセージを受信するためにアクティブなインターネット接続が必要です。ただし、Service Workerは通知をキャッシュし、接続が回復したときに表示できます。
許可が拒否された場合、そのユーザーにプッシュ通知を送信することはできません。ユーザーがブラウザの設定で手動で許可を変更するまで待つ必要があります。アプリ内通知などの代替エンゲージメント方法の実装を検討してください。
ほとんどのプッシュサービスはペイロードサイズを4KBに制限しています。ChromeとFirefoxは最大4KBをサポートし、Safariは最大2KBをサポートしています。ペイロードは最小限に抑え、必要に応じて通知を受信したときに追加データを取得してください。
はい、ほとんどのプラットフォームでブラウザが閉じているときでもプッシュ通知を受信できます。ただし、これはオペレーティングシステムとブラウザに依存します。モバイルブラウザでは、バッテリー最適化設定に基づいて制限がある場合があります。
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.