Web push
Web push works in OpenPush today, and it works through Firebase Cloud Messaging. Browsers subscribe with the Firebase JavaScript SDK and your own service worker; OpenPush stores the resulting registration token as a subscription with platform web and sends to it over the same FCM v1 path Android uses.
This page is deliberately blunt about what exists and what does not, because the gap between the two is where web integrations go wrong.
Current state, honestly
What exists
- A
webplatform, targetable likeiosandandroid. - Storage for your web credentials: site URL, VAPID public key, and a pasted
firebaseConfigobject. - An unauthenticated config endpoint,
GET /v1/apps/{app_id}/webpush-config, that publishes exactly what a browser needs to subscribe. - Delivery: any non-iOS subscription routes through FCM, so a browser's FCM web registration token receives the normal data-only payload.
- The full receipt ladder from a browser —
received,confirmed,clicked— throughPOST /v1/ingest. - A bundled browser test client shipped with the server, with a real service worker, for verifying an app end to end without writing any code.
What does not exist
- No first-party JavaScript SDK. There is no npm package, no CDN loader, no deferred command queue, no slide-down prompt, no web opt-in/opt-out lifecycle helper. You write the browser code.
- No direct Web Push Protocol sender. OpenPush does not sign VAPID JWTs, does not perform
aes128gcmpayload encryption, and never POSTs to a browser push service endpoint. Every web send goes through FCM. - The
vapidcredential kind is stored but never read by any sender. It exists for the direct sender that does not exist yet. A VAPID private key in OpenPush sends nothing. - No
webpushsub-config on outbound FCM messages. Web devices receive the same data-only payload as Android, so your service worker draws every notification.
If your requirement is "push to browsers without Firebase", OpenPush cannot do that today.
When to use
Use web push through OpenPush when you already have — or are willing to add — a Firebase web app and a service worker in your site. You get one audience, one segment language, one composer and one set of delivery stats across iOS, Android and web.
Prerequisites
- A Firebase project with a web app registered, and Cloud Messaging enabled
- The project's Web Push certificate key pair (the VAPID pair) from Firebase's Cloud Messaging settings
- A site served over HTTPS with a service worker you control
- The service account JSON for that Firebase project, uploaded under Android in OpenPush — see the warning below
- Your OpenPush app id and its SDK key
Upload the sending credential under Android, not Web.
The sender reads the Android platform credential only. A Firebase service account uploaded under Web is stored, makes the web push config endpoint work, and shows Web as ready — and is never read by any send. Web delivery then silently falls back to a server-wide credential or fails outright. Put the service account under Android, and use the Web slot for the browser-facing values. Details in platform-setup-fcm.md.
Step 1 — store your web credentials
In the console, open your app's Settings → Platforms → Web and provide:
| Value | What it is |
|---|---|
| Site URL | Your site's origin |
| VAPID public key | The public half of your Firebase Web Push certificate pair — this is the applicationServerKey browsers need |
firebaseConfig | The web app config object from the Firebase console; it is accepted as either a JavaScript object literal or JSON |
Platform credentials are uploaded through the console; there is no /v1 route for them.
Step 2 — read the config from the browser
Code
Code
| Field | Meaning |
|---|---|
app | The app id |
vapid_public | The public VAPID key, for getToken's vapidKey |
site_url | Your configured site URL |
firebase | Your firebaseConfig, re-projected through an allowlist so only the fields a browser needs are published |
sdk_key | The app's ingest-only SDK key |
needs_link_code | Whether this app registers devices through a link code |
This endpoint has no auth, deliberately. It publishes only values that are already public in any web push integration — a public VAPID key, a Firebase web config, and an SDK key whose entire capability is ingest. The service account JSON and the VAPID private key are never returned. The response is assembled field by field rather than by serializing a row, so nothing can leak by accident.
If the app has no web credential, the endpoint returns 404 with a message pointing at Settings → Platforms → Web.
Step 3 — subscribe the browser
Register your own service worker, initialize the Firebase JS SDK with the published config, and ask for a token:
Code
The subscribe flow is Firebase's, not OpenPush's. OpenPush's only interest is the token that comes out of it.
Step 4 — register the subscription with OpenPush
Post the token to the device-registration endpoint with the SDK key, with platform set to web:
Code
Code
Registration is an idempotent upsert on the token, so calling it again after a token refresh updates the existing subscription rather than duplicating it. The full body — aliases, identity verification hash, status values, collection switches — is documented in users-and-subscriptions.md and ../api-handbook/03-subscriptions-users.md.
The SDK key is safe in browser code by design; it can register a device, post receipts, post events and register Live Activity tokens, and nothing else. The REST API key must never reach a browser.
Step 5 — receive and report
Your service worker receives a data-only payload. It must draw the notification itself, and it should post the receipt ladder back:
Code
type is one of received, confirmed, clicked. Post confirmed when you actually call showNotification, and clicked from your notificationclick handler. Timestamps are first-write-wins, so a replayed receipt cannot move a stamp, and anything malformed or unknown is counted as dropped rather than failing the request.
Payload keys you will read in the worker: op_message_id (the id for receipts), title, body, image_url and deep_link when set, op_actions for buttons, and any custom data from the message.
The bundled test client
OpenPush serves a single-page browser test device at /static/testapp/. It fetches webpush-config, registers its own service worker, subscribes through Firebase, renders background pushes, and posts the full received → confirmed → clicked ladder to /v1/ingest.
Use it to prove the server side works — credentials, registration, targeting, receipts — before you write any of your own browser code. It is a diagnostic tool, not a drop-in widget: it is not versioned as a product, not embeddable, and not a substitute for the JS SDK that does not exist yet.
Limits
- Web delivery requires Firebase. There is no VAPID-direct sender.
- No first-party JavaScript SDK: no prompt UI, no opt-in lifecycle, no auto-registration, no token refresh handling. You own all of it.
- A stored VAPID private key is inert.
- Web pushes carry no
webpushFCM sub-config, so nothing is rendered for you. - The FCM data envelope is 4 KB; longer content is truncated deterministically and marked with
op_render_truncated. - Safari and other browsers that do not support Firebase Cloud Messaging for web are out of scope.
- Web subscriptions count toward the same audience, segments and frequency caps as mobile ones; there is no web-only targeting mode.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
webpush-config returns 404 | No web credential on the app | Add one under Settings → Platforms → Web |
| Config loads, browsers subscribe, nothing is ever delivered | The service account was uploaded under Web | Upload it under Android; the Web slot is not read by the sender |
getToken throws about the application server key | Wrong or truncated VAPID public key | Copy the public key from the Firebase Web Push certificate pair exactly |
Token registers but the subscription has platform android | platform omitted at registration — it defaults to android | Send "platform": "web" |
| Notifications never appear, though receipts are posted | Your service worker is not calling showNotification | Payloads are data-only; the worker must render |
| Provider Accepted is high, Confirmed Receipt is zero | The worker posts received but not confirmed | Post confirmed at the moment you show the notification |
Receipts return accepted: 0 | Wrong app id in the event, unknown token, or a stamp already written | Check the app and token fields; replays are dropped by design |
| A registered browser stops receiving after a while | FCM reported the token unregistered | The subscription is marked -10; re-subscribe and re-register |
| You added a VAPID private key and expected direct sending | There is no direct sender | Route through FCM |
FAQ
Is there an OpenPush JavaScript SDK? Not today. Web integration means your own service worker plus the Firebase JS SDK, with two OpenPush HTTP calls: register the token, post receipts.
Can I use web push without Firebase? No. Every web send leaves OpenPush through FCM.
Is it safe to expose the SDK key in browser JavaScript? Yes — that is what it is for. It can register devices and post receipts and events. Admin capability lives behind the REST key, which must stay on your server.
Why is the config endpoint unauthenticated? Everything in it is already public in any web push integration, and the response is built from an explicit allowlist rather than a database row.
Do web subscriptions support tags, external IDs and segments? Yes. A web subscription is an ordinary subscription; the whole identity and targeting model applies.
Can I send a rich notification with buttons to a browser?
The action list arrives in the payload as op_actions, but your service worker has to turn it into notification actions.
Related
- platform-setup-fcm.md
- users-and-subscriptions.md
- sending-messages.md
- segments.md
- security-and-limits.md
- ../api-handbook/03-subscriptions-users.md
- ../api-handbook/06-events-ingest.md