Web プッシュ通知:仕組みと、うまく使うための設計
Web プッシュ通知の仕組みを、Service Worker と VAPID から iOS を含む実際のブラウザー対応状況まで解説し、成果を左右する許可取得の UX と指標も扱います。
Web プッシュ通知は、設計をひとつ誤っただけで顧客に永久に締め出される可能性がある、数少ないマーケティングチャネルです。悪いタイミングで許可を求め、訪問者がブロックを押せば、そのブラウザーは二度と開きません。再プロンプトも、次のキャンペーンも、ウィンバックメールもありません。この非対称性こそ、メッセージを 1 通書く前に仕組みを理解しておく価値がある理由です。
Web プッシュ通知とは何か
Web プッシュ通知は、サーバーから購読者のブラウザーへ送られ、OS の通知センターに表示され、サイトが閉じていても届くメッセージです。最後の点が本質です。ページ上のポップアップと違い、Web プッシュは今このサイトを見ていない人に届きます。
これは 3 つの Web プラットフォーム API が連携して成り立っており、その分担がこのチャネルの挙動のほとんどを説明します。Service Worker API、Push API、Notifications API の 3 つです。
Web プッシュの内部動作
Service Worker
Service Worker は、Web アプリとブラウザーとネットワークのあいだでプロキシとして働く JavaScript のワーカーです。独立したスレッドで動き、DOM にはアクセスできず、登録したページが閉じられた後も存在し続けます。この永続性があるからこそ、タブが開いていなくてもプッシュメッセージを受け取って通知を表示できます。Service Worker は安全なコンテキストでのみ動作します。つまり HTTPS であり、開発時は http://localhost が安全なものとして扱われます。
サブスクリプション:エンドポイントと 2 つの鍵
Service Worker が有効になると、ページは registration.pushManager.subscribe() を呼び出します。ブラウザーはベンダーのプッシュサービスと通信し、次の内容を含む PushSubscription を返します。
endpoint:プッシュサービスがメッセージを受け付ける一意のケイパビリティ URLkeys.p256dh:P-256 曲線上の楕円曲線ディフィー・ヘルマン公開鍵keys.auth:認証用のシークレット
サーバーはこの 3 つを保存し、エンドポイントは秘密情報として扱ってください。これを持っている者は誰でもその購読者に送信できるからです。鍵が存在するのは、ペイロードがエンドツーエンドで暗号化されるためです。RFC 8291 がその方式を規定しています。P-256 上の ECDH 交換で共有秘密を確立し、HKDF で鍵を導出し、aes128gcm のコンテンツエンコーディングのもとで AES-128-GCM によりペイロードを封じます。プッシュサービスは、自分では読めない暗号文を中継します。
プッシュサービス
プッシュメッセージを端末へ直接送ることはできません。送り先はブラウザーベンダーが運用するプッシュサービスです。Chrome なら Google の FCM エンドポイント、Firefox なら Mozilla の autopush、Safari なら Apple のプッシュサービスです。プロトコルは RFC 8030「Generic Event Delivery Using HTTP Push」が定義しています。サーバーはサブスクリプションのエンドポイントに POST し、プッシュサービスがモバイル配信の難しい部分、つまり端末への省電力な単一接続、オフライン時のキューイング、メッセージ到着時のブラウザー起動を引き受けます。配信が保証できないのもこのためです。端末が十分に長く電源オフのままだと、メッセージは TTL に従って期限切れとなり破棄されます。
VAPID:送信者が誰かを証明する
エンドポイントが秘密であることは、セキュリティとしては薄弱です。RFC 8292 は Voluntary Application Server Identification(VAPID)を追加し、プッシュサービスがメッセージの送信元アプリケーションサーバーを識別できるようにしました。
まず NIST P-256 曲線上の ECDSA 鍵ペアを一度だけ生成します。公開鍵はブラウザーがサブスクライブする際に applicationServerKey に渡され、サブスクリプションが自社サーバーに結び付けられます。送信のたびに、サーバーは対応する秘密鍵で ES256 により署名した JWT を送ります。JWT にはプッシュサービスのオリジンを示す aud クレーム、24 時間以内の exp クレーム、任意で連絡先を示す sub クレームが含まれます。プッシュサービスが署名を検証するため、エンドポイントを盗んだだけでは購読者にスパムを送れなくなります。
配信の流れ、最初から最後まで
- ページが Service Worker を登録し、許可が得られた後に VAPID 公開鍵を渡して
subscribe()を呼び出します。 - サーバーが返却されたエンドポイントと鍵を購読者レコードに保存します。
- 送信時、サーバーは
p256dhとauthでペイロードを暗号化し、VAPID の JWT に署名し、エンドポイントに POST します。 - プッシュサービスがリクエストを認証し、暗号化されたメッセージを配信します。
- ブラウザーが
pushイベントで Service Worker を起こし、Service Worker がペイロードを復号してServiceWorkerRegistration.showNotification()を呼び出します。 - クリックされると Service Worker で
notificationclickが発火し、そこで遷移先の URL を開きます。
許可モデルが厳しい理由
サブスクリプションが何を許すのかを見てください。サイトが開いていなくても動くバックグラウンド処理と、OS の通知領域に描画する権限です。だからブラウザーは、これをオリジンごとの明示的なユーザー許可の背後に置き、多くのブラウザーは要求が本物のユーザー操作に続くことを求めます。
もうひとつの制約は意外に思われがちです。Chrome と Edge は subscribe 時に userVisibleOnly: true を必須としており、これはすべてのプッシュがユーザーに見える通知を生むという約束です。したがって、無音のバックグラウンドプッシュはこの API のサポートされた用途ではありません。Firefox も、通知を生成しないプッシュメッセージにはクォータを課します。
ブラウザーとプラットフォームの対応状況
MDN によれば、Push API は 2023 年 3 月から Baseline の widely available になっており、デスクトップの Chrome・Edge・Firefox・Safari の現行版、および Android の Chrome と Firefox で動作します。ただし、見出しよりも重要な注意点が 2 つあります。
1 つ目は、Notifications API の対応が一様ではないことです。MDN はこれを limited availability と表示しています。Notification() コンストラクターがほとんどのモバイルブラウザーで TypeError を投げるためです。スマートフォンで確実に動かす必要があるものには、ServiceWorkerRegistration.showNotification() による永続通知を使ってください。どのみち Service Worker の経路を通ることになります。
2 つ目は、通知オプションの実装が不揃いなことです。アクションボタン、バッジ、画像、requireInteraction はブラウザーと OS によって差があるため、タイトルと本文とアイコンだけでも正しく読める通知を設計してください。
iOS と iPadOS の要件
この注意点は、モバイル中心のオーディエンスに対して Web プッシュが成立するかどうかを決めます。そして、ほとんどの場所で誤って説明されています。
Apple は iOS と iPadOS 16.4 で Web プッシュを追加しましたが、動作するのはホーム画面に追加された Web アプリに限られます。WebKit の言葉を借りれば、「ホーム画面の Web アプリに Web プッシュのサポートを追加します」であり、「ホーム画面に追加された Web アプリは、プッシュ通知を受け取る許可を要求できます」です。ユーザーは共有メニューの「ホーム画面に追加」から追加し、その後、購読ボタンのタップなど直接の操作に応じて許可を要求する必要があります。
iPhone の通常の Safari タブで開いているサイトは、プッシュのサブスクリプションを作成できません。これは実際の障壁です。許可を求める前に、インストールという一手間をお願いすることになります。macOS ではもっと簡単で、macOS Ventura の Safari 16.1 が、インストール不要の通常の Web サイト向けに標準準拠の Web プッシュを追加しています。
最小限のサブスクリプション実装例
これがクライアント側のフローのすべてです。ページ読み込み時ではなく、クリックハンドラーの中に置いてください。
async function subscribeToPush(vapidPublicKey) { // プッシュと Service Worker は安全なコンテキスト(HTTPS)を必要とします。 if (!("serviceWorker" in navigator) || !("PushManager" in window)) return null;
const registration = await navigator.serviceWorker.register("/sw.js");
// ユーザー操作から呼び出すこと。1 ユーザーにつき 1 回だけです。 const permission = await Notification.requestPermission(); if (permission !== "granted") return null;
const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: vapidPublicKey, // base64url でエンコードした P-256 公開鍵 });
// エンドポイントと鍵をサーバー側に保存し、エンドポイントは秘密情報として扱います。 await fetch("/api/push/subscribe", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(subscription), });
return subscription;}sw.js の中では push イベントを処理し、self.registration.showNotification(title, options) を呼び出し、notificationclick を処理して遷移先の URL を開きます。
許可取得の UX:多くの施策が失敗する場所
ページ読み込み時に尋ねてはいけません
Lighthouse にはこれ専用の監査項目があります。「ページ読み込み時に通知の許可を求めると、その通知はユーザーやそのニーズに関連しないものになりかねません」。推奨されているのは、具体的な種類の通知を提示し、ユーザーがその種類にオプトインした後にだけ許可を求めることです。Safari 12.1 をはじめとするブラウザーはさらに踏み込み、そもそも要求の前にページへの操作を必須としました。
ソフトな事前プロンプトを使う
まず自前のページ内の案内を表示します。価値提案を明示でき、閉じられても永続的な損失がなく、そこをクリックしたときにだけ本物のブラウザープロンプトが出ます。今日ソフトプロンプトを無視した人には、来月もう一度尋ねられます。ブロックを押した人には二度と尋ねられません。これを機能させるルールは 2 つです。「最新情報をお届け」ではなく実際に何を送るのかを説明すること、そして誤タップを誘発しないことです。うっかりの許可は、即座の配信停止を生みます。
文脈の中で尋ねる
Chrome 自身の推奨は、既存のインターフェースの中にトグルをさりげなく置き、「ユーザー自身のペースで主体的に通知をオンにできるようにする」ことであり、「文脈のないプロンプトやオーバーレイ、あるいはユーザーがサイトに到着した直後の表示」を避けることです。
コマースで機能する瞬間は具体的です。売り切れ商品の「入荷したら知らせる」コントロール、配送状況の通知を提案する注文確認ページ、よく見られている商品の価格ウォッチのトグル。許可は、収集されるのではなく、名前のある便益と交換されます。
拒否は事実上永続します
通知がブロックされると、ブラウザーはその判断をオリジンに対して保存します。以降の Notification.requestPermission() の呼び出しは、何も表示せずに保存された denied を返します。MDN の例が requestPermission() を呼ぶ前に必ず Notification.permission を確認しているのはそのためです。ブロックの解除にはサイト設定を掘り進む必要があり、実際にそこまでする人はほぼいません。
誤ると、ブラウザー側が何をするか
その結果は、もはやオプトイン率が下がるだけでは済みません。
- Chrome は、許可率が極端に低いオリジンを端末種別ごとに自動でより静かな許可 UI に組み入れ、全員に対してプロンプトを抑制します。
- Chrome は、大量のプッシュと低いエンゲージメントが重なるサイトにレート制限をかけ、HTTP 429 を返します。段階は 1 日、次に 7 日、次に 14 日と進み、通知が妨げにならない日が 42 日連続して初めてリセットされます。
- Chrome は現在、ユーザーが最近やり取りしていないサイトについて、「ユーザーエンゲージメントが非常に低く、大量の通知が送られている」場合に通知許可を自動的に取り消します。Google の説明は率直です。「すべての通知のうち、ユーザーが何らかの操作をするものは 1% 未満です」。
送り方が悪いというだけで、すでに獲得した購読者を失うことがあるのです。
Web プッシュとメール・SMS の比較
| 要素 | Web プッシュ | メール | SMS |
|---|---|---|---|
| リーチ | オプトインしたブラウザーのみ | アドレスを持つ相手全員 | 番号を持つ相手全員 |
| 限界費用 | 実質ゼロ | 非常に低い | 1 通ごと、最も高い |
| 即時性 | 数秒、OS が表示 | 数分から数日、受信箱に埋もれる | 数秒 |
| メッセージ長 | タイトルと短い本文 | 無制限、リッチな装飾も可能 | 1 セグメントおよそ 160 文字 |
| 同意 | ブラウザーのプロンプトを 1 クリック | アドレスの収集、できればダブルオプトイン | 明示的で、規制も厳しい |
| 同一性 | 1 台の端末上の 1 ブラウザー | 1 人の人間 | 1 人の人間 |
| 持ち出し | 不可 | 完全にエクスポート可能 | 完全にエクスポート可能 |
戦略を変える「所有」の違い
プッシュのサブスクリプションは、1 台の端末の 1 つのブラウザープロファイルに結び付いたケイパビリティ URL です。人ではありません。同じ顧客がノート PC の Chrome とスマートフォンの Firefox を使っていれば、それは無関係な 2 つのサブスクリプションであり、本人が名乗らない限り同一人物だと知る術はありません。
持ち出しもできません。メールリストはエクスポートして明日別のプラットフォームに読み込めます。プッシュのサブスクリプションはベンダー間で移せません。鍵と VAPID の結び付きが、特定のアプリケーションサーバー鍵に対して作られているからです。
したがって Web プッシュは、自社所有チャネルの加速装置として扱い、決して代替とはしないでください。プッシュの瞬間を使ってメールアドレスや電話番号を獲得するのであって、その逆ではありません。複数チャネルを 1 つのジャーニーに束ねる方法は、マーケティングオートメーション完全ガイドで扱っています。
本当に機能するユースケース
このチャネルが報いるのは、時間的に切迫していて、個人に関連があり、ワンタップで行動できるメッセージです。
- カゴ落ち。 1 時間以内のプッシュと、後続のメールでの補強。順序設計はカゴ落ちメールガイドで扱っています。
- 再入荷通知。 最も強いケースです。ユーザー自身が知らせてほしいと明示的に頼んでいるからです。
- ウォッチ中の商品の値下げ。 同じ理屈で、関連性を自分で選んでいます。
- 配送と注文のステータス。 開封意欲が高く、苦情のリスクが低い分野です。
- 購読中のトピックの速報。 ニュース、結果、受付枠の空きなど。
うまくいかないものも同じくらい明確です。「新しい記事を公開しました」という一斉配信、差別化のない日替わりセール、タイトルと 1 行に収まらない内容、直近 20 件の通知を無視した購読者への再エンゲージメント一斉送信、そして記録として残す必要のあるトランザクション内容です。
頻度、タイミング、セグメンテーション
保守的に始めてください。購読者 1 人あたり週 1 〜 3 通とし、オプトアウト率とクリック率が維持できる場合にだけ増やします。疲労はメールより早く表れます。OS がすでに表示している通知を 1 タップでミュートできるからです。
タイミングは強みであると同時に危険でもあります。プッシュは即座に届くので、午前 2 時に送れば午前 2 時に届きます。購読時に購読者のタイムゾーンを保存または推定し、定めた時間帯の中でのみ送信してください。
セグメンテーションは、人ではなくサブスクリプションについて分かっていることに制約されます。したがって実用的な軸は行動系です。閲覧したページ、ウォッチ中の商品、カートの状態、購入からの経過、プラットフォームなど。詳しくは顧客セグメンテーションガイドをご覧ください。
Web プッシュの計測
重要な指標は 4 つあり、いずれも同じ方法で測れるわけではありません。
- 配信。 プッシュサービスがリクエストを受理したかどうか。201 は受理であって配信ではありません。404 や 410 はサブスクリプションが失効していることを意味します。
- 表示。 通知が実際に表示されたかどうか。
showNotification()の解決時に Service Worker から報告させて初めて分かります。 - クリック率。 表示数に対するクリック数。最適化する価値があるのはこの数字です。
- オプトアウト率。 送信あたりの配信停止と許可取り消し。クリック率より注意深く見てください。チャネルが死につつあることを最初に示す指標だからです。
アトリビューションの落とし穴
プッシュのアトリビューションは自分を過大評価しがちです。通知は購読者がすでに手に持っている端末に届くため、どのみち発生していたセッションの手柄を取ってしまいます。増分効果を仮定せず、ホールドアウトグループを使ってください。表示数は過少に数えられる一方でクリックはすべて記録されるため、送信数に対して算出したクリック率は成果を誇張します。さらに、サブスクリプションは人ではなくブラウザーなので、スマートフォンでクリックされてデスクトップで購入に至った場合、無関係な 2 つの出来事に見えます。チャネル横断の計測衛生についてはメールマーケティング指標ガイドで扱っています。
同意、GDPR、オプトアウト
ブラウザーの許可プロンプトは技術的なゲートです。それが自動的にマーケティングの完全な法的根拠になるわけではありません。
EU や英国の人々にマーケティングを行う場合は、プッシュをメールと同じように扱ってください。プロンプトが出る前に何を送るのかを説明し、同意が説明を受けたうえでの具体的なものになるようにします。サブスクリプションがいつどこで作られたかを記録し、無関係な操作にプッシュの同意を抱き合わせないでください。サブスクリプションを識別済みの顧客に紐づけるなら、そのデータは削除請求を含む個人データの義務の範囲に入ります。
オプトアウトの衛生も同じくらい重要です。ブロックの代わりに頻度を下げられるよう、サイト内に設定画面を用意し、実行時には PushSubscription.unsubscribe() を呼んでサーバー側のレコードも削除し、404 や 410 が返ったサブスクリプションは削除してください。MDN の指針は短く、そして正確です。ユーザーには「今後の受信を簡単にやめられる方法が提供されるべき」です。
チャネル構成の中での Web プッシュの位置づけ
Web プッシュは 3 番目のチャネルとしては優秀ですが、1 番目としては不向きです。速く、限界費用がほぼゼロで、時間に敏感な通知では他に並ぶものがない一方、端末に縛られ、持ち出せず、1 クリックで永久に失われます。
だからこそ、本当の課題はオーケストレーションになります。どのメッセージをどのチャネルに乗せるか、プッシュですでにコンバージョンした相手のメールをどう抑止するか、人の識別方法が異なる複数の面をまたいで顧客像をどう 1 つに保つか。Brevo はメールと SMS に加えて Web とモバイルのプッシュを提供しており、Tajo は Brevo の上に乗って Shopify ストア向けにそのクロスチャネルのロジックを調整します。SMS 側についてはSMS 自動化ガイドをご覧ください。
要点
- Web プッシュは 3 つの API の協調です。バックグラウンド実行のための Service Worker、サブスクリプションと転送のための Push API、表示のための Notifications API。サブスクリプションはエンドポイントと
p256dh鍵とauthシークレットで構成され、ペイロードはエンドツーエンドで暗号化され、VAPID が送信者を証明します。 - プッシュは 2023 年 3 月から Baseline の widely available ですが、iOS と iPadOS ではホーム画面に追加された Web アプリでのみ動作します。
- ページ読み込み時に許可を求めないでください。ソフトな事前プロンプトを使い、文脈の中で尋ね、ブロックはそのオリジンに対して永続することを忘れないでください。
- Chrome は現在、静かなプロンプト、レート制限、許可の自動取り消しを実施しており、送り方が悪ければすでにいる購読者を失います。
- サブスクリプションは人ではなくブラウザーであり、エクスポートもできません。まずメールリストを作り、プッシュはその加速に使ってください。