PWA Push Notifications on iOS in 2026: What Really Works

Updated:
PWA Push Notifications on iOS in 2026: What Really Works

Push notifications for PWA on iOS are one of the most discussed topics among web developers in the last two years. Apple kept this feature closed for a long time, and when it opened it, it did so with restrictions that still raise questions in practice.

In this article — a technical breakdown based on a real Kazki AI project: what works, what doesn't, and what code is needed to make everything work on iPhone.

If you are still choosing between PWA and a native app — first read PWA vs Native in 2026: comparison and a real case .

📚 Article Contents

🎯 Section 1. Why iOS blocked Push Notifications for PWA for years

Short answer: until March 2023, the Push API for PWA on iOS was deliberately disabled — regardless of the platform's technical capabilities.

Android supported Web Push since 2015. Apple added support only in iOS 16.4 (March 2023) — eight years later. The reason is not technical, but business-related: PWA with full-fledged pushes reduces the incentive to publish apps in the App Store, where Apple charges a 15–30% commission.

Before iOS 16.4, web applications on iPhone physically could not receive push notifications — not due to WebKit limitations, but due to Apple's deliberate decision not to implement this feature.

Officially, Apple explained the lack of support with privacy and UX considerations. Technically, the implementation indeed required integration with APNs (Apple Push Notification service) in a browser context — but this was not an insurmountable obstacle, as macOS Safari supported Web Push since OS X Mavericks (2013). [Wikipedia: Apple Push Notification service]

Real pressure came from outside. EU regulatory requirements under the Digital Markets Act forced Apple to reconsider its approach to web applications. In February 2023, Apple even planned to limit PWA functionality in the EU to the level of a "web bookmark" — but after public pressure from developers, it backed down and instead added Web Push for all regions simultaneously. [Brainhub: PWA on iOS — Current Status]

🎯 Section 2. iOS 16.4, 17, 18: chronology of changes — what appeared and what is still broken

Each iOS version improved PWA Push support, but the problem with random subscription "disappearance" is still not fully resolved.

iOS 16.4 opened Web Push for installed PWAs. iOS 17 enabled the relevant APIs by default in all browsers. iOS 18 did not add radical changes, but developers still report cases where push subscriptions "disappear" without an obvious reason.

iOS 16.4 is a start, not the finish. Each subsequent update partially corrected the behavior, but the stability of PWA Push on iOS still lags behind Android.

iOS 16.4 (March 2023) — opening

The first release with Web Push support for Home Screen web apps. Push notifications appeared on the Lock Screen, in the Notification Center and on Apple Watch. Badging API support was added. [WebKit: Web Push for Web Apps on iOS and iPadOS]

But a problem immediately emerged: push subscriptions stopped working after 1–2 weeks. The endpoint became inactive, and the user had to reinstall the PWA to subscribe again. [Apple Developer Forums: PWA Push Issues iOS]

iOS 17 (September 2023) — expansion

Web Push API became available by default in all browsers on iOS — not just in Safari PWA. Stability improved, but the problem of disappearing subscriptions remained for many developers. [OneSignal: iOS Web Push Setup]

iOS 18 (September 2024) — stabilization

There were no significant changes in the Web Push API. Some developers report improved delivery reliability, while others still experience the same problems with lost subscriptions after prolonged app inactivity. [Apple Developer Forums: iOS 17–18 discussion]

🎯 Section 3. Main condition: Safari only + Add to Home Screen — Chrome doesn't count

Push notifications on iOS work exclusively for PWAs installed via Safari → Share → Add to Home Screen. An open tab in any browser does not count.

Unlike Android, where Chrome can request push permissions directly in the browser, iOS requires the PWA to be installed as a separate app on the home screen. Chrome on iOS uses WebKit under the hood, and it cannot replace Safari in this process.

Web Push is not supported inside Safari on iOS — it only works for Home Screen web apps. [Apple Developer Forums]

This means that before requesting notification permission, you need to check two things: whether it's iOS, and whether the app is opened in standalone mode (i.e., installed on the home screen).


const isIOS = /iphone|ipad/i.test(navigator.userAgent);
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;

if (isIOS && !isStandalone) {
  // Show hint: "Add to home screen to receive notifications"
  showInstallPrompt();
  return;
}

If you skip this check — Notification.requestPermission() on iOS will simply return 'default' or throw an error, and the user won't understand why the "subscribe" button isn't working.

PWA Push Notifications on iOS in 2026: What Really Works

🎯 Section 4. Step-by-step: user subscription to Push on iOS

Subscription on iOS requires a user gesture — the permission request cannot be triggered automatically on page load, only in response to a user action.

Unlike Android, where permission can be requested programmatically via setTimeout, iOS Safari strictly requires Notification.requestPermission() to be called directly from a click handler. Otherwise, the request will be silently ignored or blocked. [pwa.io: Web Push with iOS Safari 16.4]

"Remember, you cannot request a push subscription without an explicit user gesture." [Apple WWDC22: Meet Web Push for Safari]

Here is the full subscription flow from a real Kazki AI project:


const VAPID_PUBLIC_KEY = 'YOUR_PUBLIC_VAPID_KEY';

async function initPushSubscription() {
    // 1. Check for support
    if (!('serviceWorker' in navigator) || !('PushManager' in window)) return;

    // 2. Check for iOS: standalone mode only
    const isIOS = /iphone|ipad/i.test(navigator.userAgent);
    const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
    if (isIOS && !isStandalone) {
        showInstallPrompt(); // show "Add to home screen" hint
        return;
    }

    try {
        const reg = await navigator.serviceWorker.ready;

        // 3. Check if already subscribed
        const existing = await reg.pushManager.getSubscription();
        if (existing) return;

        // 4. Request permission — only after user gesture!
        const permission = await Notification.requestPermission();
        if (permission !== 'granted') return;

        // 5. Create subscription
        const subscription = await reg.pushManager.subscribe({
            userVisibleOnly: true,
            applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
        });

        // 6. Save to server
        await fetch('/api/push/subscribe', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                endpoint: subscription.endpoint,
                p256dh:   subscription.toJSON().keys.p256dh,
                auth:     subscription.toJSON().keys.auth
            })
        });

    } catch (e) {
        console.error('Push error:', e.message);
    }
}

// Subscribe only on click — not automatically!
document.querySelector('#push-btn')
        .addEventListener('click', initPushSubscription);

The subscription endpoint on iOS always starts with https://web.push.apple.com/, on Android — with https://fcm.googleapis.com/. This is useful for diagnostics. [pqvst.com: Demystifying Web Push Notifications]

🎯 Section 5. Web App Manifest and Service Worker: mandatory settings

without display: standalone in manifest.json, the Push API on iOS simply won't appear in the Service Worker, even if everything else is configured correctly.

Apple requires the PWA to be declared as a "Home Screen web app" via the manifest. Without this field, registration.pushManager returns undefined on iOS. [GitHub: webpush-ios-example]

PushManager appears in the serviceWorker only after adding the site to the home screen via Safari — and only if the manifest contains display: standalone.

Minimum manifest.json for Push to work on iOS:


{
  "name": "Kazki AI",
  "short_name": "Kazki",
  "display": "standalone",
  "start_url": "/",
  "scope": "/",
  "background_color": "#ffffff",
  "theme_color": "#6366f1",
  "icons": [
    {
      "src": "/images/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/images/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Critically important: icons must be real PNG files of the correct size. A missing or inaccessible icon can block PWA installation on iOS.

Service Worker: why you cannot cache authentication and API

A common mistake is aggressive caching that breaks authentication. Here's the approach from Kazki AI: we cache only static assets, everything dynamic — we skip entirely:


self.addEventListener('fetch', event => {
    const url = new URL(event.request.url);

    // Do not cache: API, authentication, dynamic pages
    if (event.request.method !== 'GET') return;
    if (url.pathname.startsWith('/api/')) return;
    if (url.pathname.startsWith('/login') ||
        url.pathname.startsWith('/logout') ||
        url.pathname.startsWith('/oauth2')) return;
    if (url.pathname.startsWith('/dashboard') ||
        url.pathname.startsWith('/profile')) return;

    // For the rest — network-first, fallback to cache
    event.respondWith(
        fetch(event.request)
            .then(response => {
                if (response.ok) {
                    const clone = response.clone();
                    caches.open(CACHE_NAME)
                          .then(cache => cache.put(event.request, clone));
                }
                return response;
            })
            .catch(() => caches.match(event.request))
    );
});

If the authentication page is cached — the user will see an outdated version or "get stuck" in a cached state after logging out.

Learn more about common caching errors in PWA — including aggressive HTML caching and routing issues — in the article 8 critical errors when integrating PWA .

💼 Section 6. Push handler: try/catch fallback and iOS specifics from a real project

iOS sometimes sends a push event with data not in JSON format — without try/catch, the Service Worker will crash and the subscription will be automatically canceled after several such failures.

Apple documents that if a Service Worker does not display a notification after receiving a push event — iOS revokes notification permission for that site. Therefore, event.waitUntil and a correct fallback are critically important. [Apple Developer: Sending Web Push Notifications]

If the Service Worker does not display a notification after a push event — iOS treats it as a "silent push" and cancels the subscription. [dev.to: iOS push subscriptions terminated after 3 notifications]

Full push handler from Kazki AI with explanations:


self.addEventListener('push', event => {
    // try/catch — iOS sometimes sends non-JSON payload
    let data = {};
    try {
        data = event.data?.json() ?? {};
    } catch (e) {
        data = { title: 'Kazki AI', body: event.data?.text() ?? '' };
    }

    // Badging API — show counter on icon
    const badgePromise = self.navigator?.setAppBadge
        ? self.navigator.setAppBadge(data.badgeCount || 1)
        : Promise.resolve();

    // event.waitUntil — MANDATORY, otherwise iOS will cancel subscription
    event.waitUntil(
        Promise.all([
            self.registration.showNotification(data.title || 'Kazki AI', {
                body:  data.body || '',
                icon:  '/images/favicon.jpg',
                badge: '/images/favicon.jpg',
                data:  { url: data.url || '/dashboard' }
            }),
            badgePromise
        ])
    );
});

// Click on notification — open the required page
self.addEventListener('notificationclick', event => {
    event.notification.close();

    if (self.navigator?.clearAppBadge) {
        self.navigator.clearAppBadge();
    }

    // Open URL from payload or default /dashboard
    event.waitUntil(
        clients.openWindow(event.notification.data.url)
    );
});

Payload structure sent by the backend (Spring Boot + nl.martijndwars/web-push):


{
  "title": "New story is ready!",
  "body": "Your story about the dragon is already waiting",
  "icon": "/images/favicon.jpg",
  "url": "/library",
  "badgeCount": 1
}

💼 Section 7. 7 reasons why pushes don't arrive on iPhone — and how to fix it

Most problems with iOS PWA Push are related not to the server, but to how client-side code interacts with Safari's limitations.

Problems with push subscriptions on iOS are one of the most discussed topics on Apple Developer Forums. Below are seven of the most common reasons with specific solutions. [Apple Developer Forums: PWA Push Issues]

1. PWA not installed on the home screen

The most common reason. The Push API is simply unavailable until the app is opened in standalone mode. Solution: add a UI hint with instructions "Share → Add to Home Screen".

2. Service Worker does not display a notification after a push event

iOS treats a push without showNotification as "silent" and cancels the subscription after several such occurrences. Solution: always call showNotification inside event.waitUntil. [dev.to: iOS push terminated after 3 notifications]

3. VAPID subject in incorrect format

Apple requires the VAPID subject to be either a mailto: address, or a full HTTPS URL. Any other format returns 403 Forbidden from web.push.apple.com. Solution: check vapid.subject in the server configuration. [Longsight: Implementing Cross-Platform Push]

4. Subscription "disappeared" after re-authentication

If the frontend code calls subscription.unsubscribe() during logout or reauthentication — Safari will not allow subscribing again without an explicit user gesture. Solution: do not cancel the subscription on logout, only deactivate it on the server. [XenForo: Lost push subscriptions iOS PWA]

5. HTTP instead of HTTPS

Service Worker and Push API require HTTPS without exception. Even a redirect from HTTP to HTTPS can interrupt SW registration. Solution: ensure that the entire site, including static assets, is served over HTTPS.

6. Permission request not called from user gesture

Notification.requestPermission() called in setTimeout, DOMContentLoaded or any automatic code — will be silently blocked on iOS. Solution: call it exclusively within a button click handler. [pwa.io: Web Push iOS Safari 16.4]

7. Endpoint returned 410 or 404 — subscription not removed from DB

When Apple invalidates a subscription — the server receives a status 410 Gone or 404. If this endpoint is not removed from the database — subsequent attempts to send a push to this user will waste resources. Solution: handle these status codes and automatically remove the subscription.


// Spring Boot: handling invalid subscriptions
if (statusCode == 410 || statusCode == 404 || statusCode == 400) {
    pushSubscriptionRepository.deleteByEndpoint(sub.getEndpoint());
    log.info("Removed invalid subscription: {}", sub.getEndpoint());
}

💼 Section 8. Workarounds and fallback strategies

If PWA Push on iOS is not suitable for your use case — email and in-app notifications remain a more reliable alternative with a higher delivery rate.

PWA Push on iOS works, but has limitations: installation is required, possible loss of subscriptions, lower delivery rate compared to native. For critical notifications, it's worth having a fallback strategy.

A reliable notification system is not a choice between Push and email, but a combination of both with graceful degradation.

Email as the primary fallback

Email has a delivery rate of about 90–95% and does not depend on the iOS version or PWA installation status. For transactional notifications (payment, confirmation, important events), email remains a more reliable channel.

In-app notifications

An unread counter in the header, an event feed inside the app — this is what the user is guaranteed to see on the next PWA opening, regardless of the push subscription status.

Combined approach with Kazki AI

Kazki AI uses a three-tier system:

  1. Push notifications — for installed PWAs on iOS and Android
  2. Email — for all users as a fallback
  3. Badge on the icon (Badging API) — for installed PWAs

This allows reaching 100% of the audience regardless of whether the user has installed the PWA on the home screen.

Learn more about choosing between PWA and a native app — PWA vs Native in 2026: comparison and a real case .

❓ Frequently Asked Questions (FAQ)

Does Web Push work on iOS without PWA installation?

No. The Push API on iOS is exclusively available for Home Screen web apps — i.e., websites added via Safari → Share → Add to Home Screen. An open tab in Safari or any other browser does not have access to PushManager. [WebKit Blog: Web Push for Web Apps on iOS]

Does PWA Push work via Chrome on iPhone?

No. Chrome, Firefox, and any other browser on iOS use WebKit as their engine — this is an Apple requirement. Therefore, all browsers on iOS have the same limitations regarding the Push API. Push only works via Safari provided the PWA is installed on the home screen. [Apple Developer Forums: Push in Chrome iOS]

From which iOS version is Web Push supported for PWA?

From iOS 16.4 (March 2023). Versions prior to 16.4 do not support Web Push for PWA regardless of browser or settings. According to StatCounter, as of early 2026, over 95% of iPhones worldwide run on iOS 16 or newer versions. [WebKit Blog: Web Push iOS 16.4]

Why does the subscription disappear after a few days?

There are several reasons. First — iOS cancels the subscription if the Service Worker received push events but did not display a notification. Second — if the PWA has not been opened for a long time, Safari may clear the Service Worker's state. Solution: always call showNotification in event.waitUntil and check the subscription's relevance every time the app is opened. [dev.to: iOS push subscriptions terminated]

What is the delivery rate of PWA Push on iOS compared to Android?

Based on the experience of teams that have publicly shared data — the delivery rate on iOS is approximately 70–85% compared to 90–95% on Android. The difference is due to additional Safari limitations: stricter background process management, possible loss of subscription after prolonged inactivity. [OneSignal: iOS Web Push delivery]

Can a push be sent without a backend library?

Technically yes — the Web Push Protocol is an open standard. But self-implementing VAPID signing and payload encryption is complex and prone to errors. For Java/Spring Boot, the library nl.martijndwars:web-push, is recommended for Node.js — web-push.


<!-- Maven: dependency for Java/Spring Boot -->
<dependency>
    <groupId>nl.martijndwars</groupId>
    <artifactId>web-push</artifactId>
    <version>5.1.1</version>
</dependency>

How to generate VAPID keys?

The easiest way — via web-push CLI:


npx web-push generate-vapid-keys

The public key is used on the frontend for subscription, the private key — on the backend for signing each push request. Never publish the private key in a public repository.

✅ Conclusions: when PWA Push on iOS is sufficient, and when native is better

PWA Push on iOS works and is suitable for most content and SaaS products. For critical notifications in fintech, medicine, or e-commerce with a large share of iOS audience, it is worth either combining with email or considering native.

With iOS 16.4, the technical barrier has been removed. What remains is operational: the user must install the PWA on the home screen — and this is the main limitation that cannot be solved at the code level.

PWA Push on iOS is not an "either it works, or it doesn't" situation. It's a tool with specific operating conditions. If these conditions are met — it works stably.

PWA Push on iOS is suitable if:

  • Your product is content-based: news, media, education, SaaS
  • The audience is loyal and willing to install the PWA on the home screen
  • Notifications are informational, not critical (reminders, news, updates)
  • Email is available as a fallback for those who haven't installed the PWA
  • The budget does not allow for parallel native app development

Consider native or hybrid if:

  • Notifications are critical: banking transactions, medical alerts
  • The share of iOS in your audience exceeds 60%
  • Background processes or real-time geolocation are required
  • App Store presence is part of the marketing strategy

Conclusion from Kazki AI's experience

In Kazki AI, PWA Push on iOS works satisfactorily for informational notifications — "new story is ready", "subscription reminder". The delivery rate is lower than on Android, but acceptable for a product where delivery criticality is not high. Email remains the primary channel for transactional notifications.

If you are still choosing between PWA and a native app and haven't decided on the architecture — a detailed comparison with real figures is in the article PWA vs Native in 2026: comparison and a real case .

📖 Sources

Останні статті

Читайте більше цікавих матеріалів

PWA Push-сповіщення на iOS у 2026: що реально працює

PWA Push-сповіщення на iOS у 2026: що реально працює

Push-сповіщення для PWA на iOS — одна з найбільш обговорюваних тем серед веб-розробників останніх двох років. Apple довго тримала цю функцію закритою, а коли відкрила — зробила це з обмеженнями, які досі викликають питання на практиці. У цій статті — технічний розбір на...

ІІ-рев'ю коду 2026: Anthropic vs OpenAI vs GitHub Copilot — хто виграє гонку автоматизації

ІІ-рев'ю коду 2026: Anthropic vs OpenAI vs GitHub Copilot — хто виграє гонку автоматизації

Протягом останніх двох років ІІ навчився писати код. Тепер він навчився його перевіряти. Три найбільші гравці ринку — Anthropic, OpenAI і GitHub — запустили продукти для автоматизації код-рев'ю майже одночасно, але з принципово різними підходами.Спойлер: переможця в цій гонці визначить не...

Під капотом Claude Code Review: мультиагентна архітектура 2026

Під капотом Claude Code Review: мультиагентна архітектура 2026

Статичний аналіз і лінтери існують десятиліттями — і все одно пропускають баги, які потрапляють у production. З появою ІІ-генерації коду проблема загострилась: обсяг дифів зростає, а інструменти перевірки залишились тими самими.Спойлер: Claude Code Review вирішує цю задачу через мультиагентну...

Anthropic запустила мультиагентну перевірку коду: що це означає для розробників

Anthropic запустила мультиагентну перевірку коду: що це означає для розробників

Штучний інтелект навчився писати код швидше, ніж люди встигають його перевіряти. Черга на код-рев'ю розтягнулась до кількох днів, а якість перевірок впала — просто тому що рецензентів фізично не вистачає. Спойлер: Anthropic вирішила автоматизувати сам процес рев'ю: новий інструмент...

Proof of Personhood: навіщо світу потрібно доводити що ти людина

Proof of Personhood: навіщо світу потрібно доводити що ти людина

У 2026 році питання «ти людина чи бот?» перестало бути технічною формальністю і стало інфраструктурною проблемою інтернету. Генеративний ШІ знищив більшість методів верифікації, розроблених за останні 25 років. Ця стаття — аналіз того, чому це сталось, що пропонує ринок і де проходить межа між...

World Orb і приватність: ризики біометрії райдужки

World Orb і приватність: ризики біометрії райдужки

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