Blog
Browser Push Notifications: 85% Open Rates, Zero Monthly Cost
April 29, 2026 · 10 min read
Email open rates average 21%. Social media organic reach is under 5%. Push notification open rates average 85%. Not a typo — eighty-five percent. Browser push notifications are the highest-engagement marketing channel available, and you do not need OneSignal, PushEngage, or any other SaaS platform to use them.
The VAPID (Voluntary Application Server Identification) protocol lets you self-host push notifications using nothing but a service worker, the Web Push API, and a set of free cryptographic keys. After deploying self-hosted push across 14 sites in our network, we are getting 85% open rates at zero monthly cost.
How Web Push Works
Web push notifications are browser-native. When a user grants permission, your site registers a subscription with the browser's push service (Firebase Cloud Messaging for Chrome, Mozilla Push Service for Firefox, Apple Push Notification service for Safari). You store the subscription endpoint and keys on your server. When you want to send a notification, you send an encrypted payload to the push service, which delivers it to the user's browser — even if they are not on your site.
The user sees a native operating system notification — the same kind they get from native apps. On mobile, it appears in the notification tray. On desktop, it appears as a system notification. The user taps it and lands on your page.
The engagement rate is extraordinary because push notifications bypass every noise layer: the inbox, the social feed, the search results. The notification appears directly on the user's device with a sound or vibration. There is nothing between your content and the user's attention.
Why Self-Hosted Beats SaaS
Push notification SaaS platforms — OneSignal, PushEngage, Pusher — charge $9-$99+ per month based on subscriber count. They provide a dashboard, subscriber management, and sending infrastructure.
Here is what they actually do under the hood:
- Generate VAPID keys (a one-time operation)
- Register a service worker on your site
- Store subscription endpoints in a database
- Send encrypted payloads to browser push services
Every step of this is available as a free, open-source operation. The VAPID protocol is a web standard. The Push API is a browser standard. The encryption libraries are open-source. The push services (FCM, Mozilla, APNs) are free.
You are paying $29/month for a dashboard that wraps free infrastructure. For a small business on a $20/month budget, that is an absurd allocation.
The Self-Hosted Implementation
Here is the complete stack, deployed across our network at zero cost.
Step 1: Generate VAPID keys.
npx web-push generate-vapid-keys
This produces a public key and a private key. Store the private key securely (environment variable). The public key goes in your client-side code.
Step 2: Create the service worker.
Create a sw.js file in your site's root directory:
self.addEventListener('push', function(event) {
const data = event.data ? event.data.json() : {};
const title = data.title || 'New Post';
const options = {
body: data.body || 'Check out our latest content.',
icon: '/images/icon-192.png',
badge: '/images/badge-72.png',
data: { url: data.url || '/' }
};
event.waitUntil(self.registration.showNotification(title, options));
});
self.addEventListener('notificationclick', function(event) {
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data.url));
});
Step 3: Build the subscription flow.
On your site, add a "Subscribe to notifications" prompt. When the user grants permission:
async function subscribeToPush() {
const registration = await navigator.serviceWorker.register('/sw.js');
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
// Send subscription to your server
await fetch('/api/subscribe', {
method: 'POST',
body: JSON.stringify(subscription),
headers: { 'Content-Type': 'application/json' }
});
}
Step 4: Store subscriptions.
The subscription endpoint and keys need to be stored. For static sites, I use a Cloudflare Worker with Workers KV (free tier: 100,000 reads/day, 1,000 writes/day) as the storage backend:
// Cloudflare Worker - /api/subscribe
export default {
async fetch(request, env) {
if (request.method === 'POST') {
const subscription = await request.json();
const id = crypto.randomUUID();
await env.PUSH_SUBS.put(id, JSON.stringify(subscription));
return new Response('Subscribed', { status: 201 });
}
}
};
Step 5: Send notifications.
When you publish new content, trigger a send:
const webpush = require('web-push');
webpush.setVapidDetails(
'mailto:you@yourdomain.com',
VAPID_PUBLIC_KEY,
VAPID_PRIVATE_KEY
);
// For each subscription in your KV store:
await webpush.sendNotification(subscription, JSON.stringify({
title: 'New Post: Browser Push Notifications Guide',
body: 'Learn how to self-host push notifications for free.',
url: '/blog/web-push-vapid-free/'
}));
This can be triggered manually, by a CI/CD post-deploy hook, or by a scheduled Cloudflare Worker that checks for new content in your RSS/JSON feed.
The Permission UX
The biggest failure mode for push notifications is the permission prompt. If you show a browser permission dialog the moment a user arrives on your site, they will deny it — and you cannot ask again.
Best practices from our deployment:
Delay the prompt. Do not ask on the first page load. Wait until the user has visited at least two pages or spent at least 30 seconds on the site. This filters for engaged users who are more likely to accept.
Use a soft prompt first. Before triggering the browser's native permission dialog, show a custom UI element (a banner, a slide-in, a button) that explains what they will get: "Get notified when we publish new analysis — one notification per week, unsubscribe anytime." If they click "Yes," then trigger the browser permission dialog.
Explain the value. "Never miss a new post" is weak. "Get our weekly insurance rate update and state-by-state data before it hits social media" is specific and valuable.
Respect "No." If the user dismisses your soft prompt, do not show it again for at least 30 days. Aggressive re-prompting damages trust and increases hard denials.
Our acceptance rate using this approach: 8-12% of visitors shown the soft prompt accept it. Of those, 92% accept the browser permission dialog. This gives us a net subscription rate of approximately 7-11% of prompted visitors.
The Numbers
After six months of self-hosted push on 14 sites:
- Total subscribers: 3,400 across all sites
- Average open rate: 85% (measured by notification display acknowledgment)
- Average click-through rate: 28% (from notification to site)
- Monthly cost: $0 (Cloudflare Workers free tier, VAPID keys are free, push services are free)
- Unsubscribe rate: 2.1% per month (low — because we only send 1-2 notifications per week)
Compare this to email marketing: 3,400 email subscribers with a 21% open rate and a 2.5% CTR would generate 90 visits per send. 3,400 push subscribers with an 85% open rate and a 28% CTR generate 952 visits per send. That is 10x the traffic from the same subscriber base.
Browser Support
Web Push is supported in Chrome, Edge, Firefox, Opera, and Samsung Internet. Safari added support in Safari 16.4 (2023) with some limitations. As of April 2026, web push reaches approximately 88% of desktop users and 76% of mobile users.
The key limitation is iOS: Safari on iOS supports web push only for sites added to the Home Screen. This means iOS push notifications require the user to "install" your site as a PWA (Progressive Web App) first. This is an extra step, but it also means iOS push subscribers are extremely engaged — they liked your site enough to add it to their home screen.
Integrating with Your Publish Pipeline
The most effective push notification system is automated: when you publish a new post, a notification goes out to all subscribers without manual intervention.
For our Eleventy-based sites, the pipeline is:
- New post is committed and pushed
- Netlify builds and deploys the site
- A post-deploy webhook triggers a Cloudflare Worker
- The Worker checks the JSON Feed for new entries since the last check
- If a new entry exists, the Worker sends push notifications to all stored subscriptions
- The notification includes the post title, excerpt, and URL
The entire pipeline is automated and free. New content goes live, subscribers are notified, traffic arrives — no manual send, no email composition, no social media posting required.
The complete push notification deployment — including the permission UX framework, the automated send pipeline, and the subscriber management system — is in The $20 Dollar Agency by J.A. Watte. Chapter 22 covers notification strategy for small businesses. For deploying push across a multi-site network, see The $100 Network.