РозробкаБудь-який рівень

PWA

Прогресивний вебзастосунок, або PWA, — це сайт із service worker і вебманіфестом, який можна встановити, який працює офлайн і поводиться як застосунок. SDK для PWA не існує: що може встановлений вебзастосунок, залежить від браузера й платформи, і саме розрив між Chrome на Android і Safari на iPhone визначає більшість продуктових рішень. Сторінка починається з шістнадцяти можливостей і того, де кожна працює в жовтні 2026 року; далі — service worker і оновлення, яке не приходить, стратегії кешування й сховище, яке можуть очистити, встановлення на чотирьох платформах, що дозволяє Apple і як це змінювалося, питання магазинів застосунків, п’ять задокументованих кейсів і те, де все це вироджується.

Що це
Service worker, маніфест і HTTPS
Найбільший розрив
iPhone: push лише після встановлення; немає фонової синхронізації, Bluetooth і підказки встановлення
Дистрибуція
Посилання; виправлення виходить із наступним деплоєм

Куди дістає вебзастосунок у 2026 році — шістнадцять можливостей, платформа за платформою

PWA — це не одна технологія з одним рівнем підтримки. Це набір можливостей браузера, і кожен браузер реалізує свою підмножину. Тож питання, яке насправді має команда, — «чи можна зробити це вебзастосунком?» — не має однієї відповіді. Відповідь є для кожної можливості й кожної платформи. На iPhone відповідь ще й змінюється, щойно користувач додає сайт на домашній екран, бо частина можливостей існує лише для встановлених вебзастосунків. Кожен браузер на iOS використовує рушій від Apple, тож користувач iPhone не обійде цього, встановивши Chrome. Ось шістнадцять речей, які зазвичай потрібні застосунку, де кожна працює станом на жовтень 2026 року і що робити там, де ні. Наступні розділи посилаються на рядки за номерами.

#Що потрібно застосункуAndroid · ChromeiPhone · Safari, на домашньому екраніДесктопЯкщо цього немає
1Працювати офлайн (service worker і Cache API)ТакТак, з iOS 11.3 (2018)Усі сучасні браузериЦе база — спроєктуйте офлайн-стан (розділ 3)
2Тримати дані на пристрої (IndexedDB, файли в OPFS)Так, до 60% дискаТак — але у вкладці Safari дані видаляють після семи днів користування без візиту; встановлені застосунки — винятокТак; Firefox дає сайту щонайбільше 10 ГіБ, якщо сховище не зроблено постійнимТримати джерело правди на сервері
3Відкриватися у власному вікні, без інтерфейсу браузераТак, після встановленняТак; з iOS 26 кожен сайт, доданий на домашній екран, відкривається як вебзастосунокChrome і Edge встановлюють; Safari 17 додає в Dock; Firefox 143 закріплює на панелі задач WindowsЗакладка теж працює
4Камера, мікрофон, геолокаціяТак, після запиту дозволуТак, після запиту дозволуТак—
5Приймати платежіPayment Request, Google PayApple Pay і Payment RequestТакЗвичайна форма оплати
6Системне меню «Поділитися» (navigator.share)ТакТакChrome, Edge і Safari; Firefox — лише на AndroidКнопка «Копіювати посилання»
7Не давати екрану згаснути (Wake Lock)ТакТак, з iOS 18.4Так; Firefox — з версії 126—
8Push-сповіщенняТак, у браузері чи після встановленняЛише після додавання на домашній екран (iOS 16.4, березень 2023), і запит має йти після натисканняТак; Safari на Mac — з версії 16Email чи SMS для всього критичного
9Лічильник на іконці (setAppBadge)Ні — Android натомість показує крапки сповіщеньТак, після встановлення (iOS 16.4)Chrome і Edge після встановлення; Safari 17Лічильник усередині застосунку
10Запропонувати встановлення зі сторінки (beforeinstallprompt)ТакНі — користувач мусить сам знайти «Поділитися» → «На домашній екран»Chrome і Edge; не Safari й не FirefoxЕкран з інструкцією для кожної платформи
11Сховище, яке ніколи не очищають (navigator.storage.persist())Надається мовчки, залежно від того, наскільки користувач активнийНадається мовчки, наприклад встановленим застосункамFirefox питає користувачаСинхронізація із сервером (рядок 2)
12Надіслати відкладені записи, коли повернеться зв’язок, навіть коли застосунок закрито (Background Sync)ТакНіЛише Chrome і EdgeСтавити записи в чергу в IndexedDB і відтворювати, коли застосунок відкриють наступного разу
13Оновлювати вміст у фоні за розкладом (Periodic Background Sync)Лише встановлені застосунки; інтервал обирає браузерНіЛише Chrome і EdgeОновлювати під час відкриття; тихий push не замінить, бо кожен push мусить показати сповіщення
14Великі завантаження, що переживають закриття застосунку (Background Fetch)ТакНіЛише Chrome і EdgeЗавантажувати, поки застосунок відкритий, частинами з докачуванням
15Пристрої Bluetooth, USB, NFC і послідовного портуBluetooth, USB, NFC, а з Chrome 148 — і послідовний портЖодногоBluetooth, USB, послідовний порт і HID у Chrome та Edge; послідовний порт у Firefox 151 — через доповненняНативний або супутній застосунок
16Отримувати поширене й відкривати файли із системи (Share Target, File Handling)Share Target — такНіShare Target і File Handling у Chrome та EdgeКнопка завантаження файлу

Service worker: проксі, якого сторінка не бачить і не обійде

Усе, що PWA вміє, а звичайний сайт — ні, починається з одного механізму. Service worker — це скрипт, який браузер виконує окремо від сторінки й без доступу до неї. Після реєстрації він отримує подію fetch для кожного запиту сторінок у своїй області — HTML, скриптів, зображень, викликів . Для кожного він вирішує, що відповісти: щось із кешу, щось із мережі або щось, що збудує сам. Сторінка не може відрізнити одне від іншого. Область — це тека, у якій лежить файл воркера, тож воркер за адресою /sw.js керує всім сайтом, а за адресою /app/sw.js — лише /app/. Service worker працює лише через HTTPS, за винятком localhost під час розробки.

Service worker — це скрипт, який браузер тримає поза сторінкою, і крізь нього проходить кожен запит сторінки. Для кожного запиту він вирішує, відповісти з кешу чи піти в мережу, — і саме це взагалі уможливлює офлайн, миттєві повторні завантаження та фонову синхронізацію. Той самий задум створює й проблему оновлення: новий воркер встановлюється, але чекає, доки закриють усі вкладки зі старим, тож випущене виправлення не доходить до користувачів на наступному завантаженні, якщо сторінка не обробляє оновлення свідомо.

У воркера є життєвий цикл, і більшість проблем у продакшені походить саме з нього. Браузер перевіряє, чи є нова версія, під час кожної навігації на сторінку в області, а також під час подій на кшталт push, якщо не перевіряв протягом останніх 24 годин. Починаючи з Chrome 68, а також в інших сучасних браузерах, ця перевірка ігнорує -заголовки кешування для самого файлу воркера, хоча не для скриптів, які він імпортує. Якщо відрізняється бодай один байт, браузер завантажує й встановлює новий воркер. А тоді новий воркер чекає. Типово він бере керування лише тоді, коли жодною вкладкою вже не керує старий. До того старий воркер далі відповідає — зі старих кешів.

Браузер встановлює новий service worker, щойно помічає зміну, але типово новий воркер чекає, доки жодною вкладкою не керуватиме старий. Перезавантаження цього очікування не завершує: під час навігації нова сторінка створюється раніше, ніж зникає стара, тож старий воркер ніколи не втрачає останнього клієнта, і користувач, що тримає відкритою одну вкладку, — чи застосунок на домашньому екрані, який ніколи не закривають повністю, — може лишатися на старій версії днями. Виправлення — обробляти оновлення свідомо: виявити воркер, що чекає, сказати користувачеві, що оновлення готове, і після його натискання викликати skipWaiting та перезавантажити сторінку, щоб нова версія взяла керування в мить, яку обрав користувач.

Продумайте шлях назад ще до першого воркера. Якщо до користувачів потрапив зламаний воркер, виправлення — задеплоїти воркер, який нічого не робить, — без обробника fetch, із skipWaiting() під час встановлення, — рівно за тією самою адресою. Тоді запити проходять так, ніби воркера немає. Перейменуйте файл — і зламаний воркер лишиться зареєстрованим поруч із новим і далі відповідатиме. Заголовок відповіді Clear-Site-Data: "storage" теж знімає реєстрацію воркерів, але він стирає все сховище сайту й підтримується не скрізь, тож це запасний варіант, а не план. І нарешті, пишіть воркер бібліотекою, а не руками. Workbox (7.4), Serwist, vite-plugin-pwa і service worker від Angular генерують список файлів для передкешування з результату збірки, тож список і хеші версій лишаються правильними, поки змінюється бандл.

Кеш і сховище: наскільки застарілою може бути відповідь і скільки вона проживе

Стратегію кешування обирають для кожного виду запитів, а не раз на весь сайт, і вибір зводиться до одного питання: наскільки застарілою може бути ця відповідь? Бандл JavaScript, у назві файлу якого є хеш його вмісту, ніколи не застаріває — нова версія отримує нову назву, — тож він може братися з кешу завжди. Баланс рахунку не має застарівати ніколи, тож він мусить приходити з мережі або не приходити зовсім. Більшість запитів — десь посередині. Три стратегії покривають більшість із них, і відрізняються вони тим, хто відповідає першим.

Service worker відповідає на кожен запит за стратегією, обраною для такого типу запитів, і вибір зводиться до того, наскільки застарілою може бути відповідь. «Спершу кеш» відповідає миттєво й офлайн і коректна для файлів, чия назва змінюється разом із вмістом, як-от бандли з хешем. «Спершу мережа» показує актуальні дані, коли є зв’язок, і відкочується до останньої копії, коли зв’язку немає, — це для даних API та сторінок. Stale-while-revalidate відповідає з кешу одразу й оновлює його у фоні, тож користувач бачить учорашню копію зараз, а сьогоднішню — наступного разу: добре для аватарів і стрічок, погано для балансу чи ціни.
ЗапитСтратегіяЩо користувач бачить офлайнПастка
Скрипти, стилі, шрифти з хешем у назвіПередкешування під час встановлення; спершу кешОболонку застосунку, миттєвоindex.html без хешу в режимі «спершу кеш» закріплює кожного користувача на одному релізі
Навігація між сторінками (HTML)Спершу мережа з таймаутом у кілька секунд, потім сторінка з кешу або офлайн-сторінкаОстанню версію сторінки або сторінку, що каже про офлайнБез таймауту: на слабкому сигналі користувач дивиться на порожній екран замість сторінки з кешу
Читання з APIСпершу мережа; stale-while-revalidate там, де вчорашні дані — нормальноОстанні дані з позначкою, коли їх отриманоСтарі дані, показані як актуальні, без часу отримання
Записи в APIЛише мережа; офлайн — черга в IndexedDB і повторне надсиланняЗміну з позначкою «очікує»Повтор, що спрацював двічі й двічі списав гроші, — надсилайте з кожним записом ключ ідемпотентності
Зображення й шрифти з іншого джерела без CORSStale-while-revalidate з лімітом розміру — або зовсім ніКопію з кешуНепрозора відповідь ховає свій статус, тож помилку можна закешувати назавжди, а Chrome рахує кожну як щонайменше 7 МБ квоти
Вхід, платежі, персональні даніЛише мережаЗрозумілу помилкуПерсональні відповіді, що лишилися в кеші на спільному пристрої

Записи потребують найбільшої уваги, бо офлайн-запис — це обіцянка. Звичний патерн — зберегти зміну локально, показати її як таку, що очікує, і надіслати, коли повернеться зв’язок. У Chrome і Edge Background Sync може надіслати її навіть після закриття застосунку (рядок 12). Скрізь інде повторне надсилання стається наступного разу, коли застосунок відкриють. Так чи інакше сервер може отримати той самий запис двічі — раз від спроби, що відвалилася за таймаутом, і раз від повтору, — тож кожен запис має нести ключ, який дозволяє серверу вважати повтор тим самим запитом. Операцію, що дає той самий результат, скільки б разів її не виконали, називають ідемпотентною (), і офлайн-записи безпечні лише тоді, коли вони такі.

Що браузер має право видалити

БраузерСкільки може зберігати один сайтКоли його видаляють`persist()`
Chrome, EdgeДо 60% дискаСпершу сайти, якими користувалися найдавніше, коли закінчується місце на дискуНадається чи відхиляється мовчки, залежно від активності
Firefox10% диска або 10 ГіБ на групу сайтів; 50% для постійного сховищаСпершу найдавніше використані, якщо сховище не постійнеПитає користувача
Safari, у вкладціБлизько 60% дискаУсе, що записали скрипти, після семи днів користування Safari без візиту; а також за браку місцяНадається чи відхиляється мовчки
Safari, застосунок на домашньому екраніБлизько 60% дискаНе за правилом семи днів; але за браку місця — такІмовірніше буде надано

Офлайн — це стан, який має бути в інтерфейсі, а не технічна деталь. Показуйте, що застосунок офлайн. Позначайте закешовані дані часом, коли їх отримано. Показуйте список змін, що чекають на надсилання, і кажіть, що сталося, коли якась не пройшла. Заздалегідь вирішіть, що буде, коли той самий запис змінили на двох пристроях, поки обидва були офлайн: перемагає останній запис, сервер об’єднує чи вибирає користувач. Застосунок, що мовчки показує тижневі дані як актуальні, гірший за той, що зізнається, що він офлайн.

Встановлення: що означає «встановити» на чотирьох платформах

Друга половина PWA — вебманіфест: невеликий -файл, на який посилається кожна сторінка і який дає застосунку назву, іконки, стартову адресу й режим показу. Поставте display у standalone — і встановлений застосунок відкриється без адресного рядка й вкладок, а це й є більша частина того, що робить його схожим на застосунок, а не на закладку. Chrome пропонує встановлення, коли сайт працює через HTTPS, а в маніфесті є name чи short_name, іконки на 192 і 512 пікселів, start_url і значення display, — після того як користувач клацнув на сторінці й провів на ній близько 30 секунд. Починаючи з Chrome 108 на Android і 112 на десктопі, для встановлення з меню браузера service worker узагалі не потрібен; підказка встановлення його все ще шукає.

«Встановити» означає чотири різні результати. На Android Chrome створює WebAPK — справжній пакет Android, який з’являється серед застосунків і в їхніх налаштуваннях і може відкривати посилання зі своєї області. На iPhone користувач додає сайт із меню «Поділитися», і з iOS 26 кожен доданий так сайт відкривається як вебзастосунок; у нього власні cookies і сховище, окремі від Safari, тож користувач, що увійшов на сайті, відкриває іконку й бачить, що не увійшов. Десктопні Chrome і Edge встановлюють сайт у власне вікно й можуть зареєструвати його для файлів і посилань. Safari на Mac додає його в Dock. Лише браузери на Chromium дають сторінці самій запропонувати встановлення, тож для решти платформ потрібні письмові інструкції.

«Встановити» дає чотири різні результати. На Android Chrome створює WebAPK — справжній пакет Android, згенерований з маніфесту, — тож застосунок з’являється серед застосунків і в системних налаштуваннях і відкриває посилання зі своєї області замість браузера. На iPhone користувач додає сайт із меню «Поділитися». З iOS 26, з вересня 2025 року, кожен доданий так сайт типово відкривається як вебзастосунок — із маніфестом чи без. Вебзастосунок на iPhone має власні і сховище, окремі від Safari: користувач, що увійшов на сайті, відкриває нову іконку й бачить, що не увійшов, тож перший екран після встановлення має бути входом, який не схожий на баґ. Chrome і Edge на десктопі встановлюють сайт у власне вікно. Safari на Mac, з версії 17, додає його в Dock. Firefox 143 у вересні 2025 року почав закріплювати сайти на панелі задач Windows.

Лише браузери на Chromium дають самій сторінці запропонувати встановлення (рядок 10). Вони генерують подію beforeinstallprompt, яку сторінка може притримати й відтворити зі своєї кнопки «Встановити» в слушний момент. Скрізь інде встановлення — це пункт меню, який користувач мусить знайти сам, тож продукт, якому потрібні встановлені користувачі, пише короткі інструкції для кожної платформи й показує їх тоді, коли користувач найімовірніше погодиться, — після другого візиту, а не на першому екрані. Стандартний спосіб для сторінки попросити вже на підході: 21 вересня 2026 року Chrome оголосив намір випустити Web Install API — navigator.install() і елемент <install> — у Chrome 156, спершу на десктопі. WebKit заявив, що проти, бо встановлення має починатися в інтерфейсі самого браузера, тож не плануйте під нього фічу для iPhone.

Поле маніфестуЩо робитьХто його читає
name, short_nameНазва застосунку й коротша — для підпису іконкиКожен браузер, що встановлює
iconsІконки кількох розмірів; "purpose": "maskable" для іконок, які система може обрізати під власну формуChromium; Safari — лише коли немає apple-touch-icon
start_url, scopeДе відкривається застосунок і які адреси вважаються його частиноюКожен браузер, що встановлює
displaystandalone — вікно застосунку; minimal-ui лишає кілька елементів браузераКожен браузер, що встановлює
idСтала ідентичність, щоб пізніша зміна start_url не створила другий застосунокChromium; Safari — з 16.4 на iOS
shortcutsДії після довгого натискання на іконкуChromium; Safari 17.4 на Mac
share_targetЗ’являтися в системному меню «Поділитися» як отримувач (рядок 16)Chromium
file_handlers, protocol_handlersВідкривати в застосунку файли певного типу чи посилання певної схеми (рядок 16)Chromium на десктопі

iPhone: що дозволяє Apple і як до цього дійшло

Рядки 8–11 і порожні клітинки iPhone у рядках 12–16 мають одну причину. На iOS кожен браузер — Safari, Chrome, Firefox, Edge — мусив використовувати рушій WebKit від Apple, тож те, що реалізує WebKit, — це те, що може веб на iPhone. Правило почало слабшати: з iOS 17.4 браузери в ЄС можуть використовувати власні рушії, а з iOS 26.2, з грудня 2025 року, — і браузери в Японії. Але, за власною заявою Apple 2024 року, вебзастосунки на домашньому екрані збудовано безпосередньо на WebKit, і на момент написання жоден великий браузер не постачає на iPhone власного рушія. Плануючи, виходьте з WebKit. Історія пояснює, чому команди раз у раз на цьому спотикаються:

КолиiOSЩо змінилося для вебзастосунків
Червень 2007iPhone OS 1Apple представила вебзастосунки в Safari як спосіб створювати програми для iPhone; нативний SDK з’явився в березні 2008
Березень 201811.3Service worker і маніфест — уперше офлайн-вебзастосунки на iPhone (рядок 1)
Березень 202013.4Safari видаляє все сховище, записане скриптами, після семи днів користування без візиту; застосунки на домашньому екрані — виняток (рядок 2)
Березень 202316.4Web Push і лічильники для застосунків на домашньому екрані; інші браузери можуть додавати сайти на домашній екран (рядки 8–9)
Лют.–бер. 202417.4Бета-версії перетворили застосунки на домашньому екрані в ЄС на ярлики Safari; рішення скасували 1 березня
Березень 202518.4Declarative Web Push, що показує сповіщення без виконання JavaScript; Wake Lock у застосунках на домашньому екрані (рядок 7)
Вересень 202526Кожен сайт, доданий на домашній екран, типово відкривається як вебзастосунок; маніфест не обов’язковий (рядок 3)
Грудень 202526.2Інші рушії браузерів дозволено в Японії, як у ЄС з 17.4

Push на iPhone має три правила, крім «лише для встановлених» (рядок 8). Запит дозволу може з’явитися лише у відповідь на пряму дію, як-от натискання кнопки «Увімкнути сповіщення», — і ніколи під час завантаження сторінки. Кожен push мусить показати сповіщення, яке бачить користувач: WebKit відкликає підписку сайту, чий воркер отримав push і нічого не показав, тож тихого push для фонових оновлень у вебі немає (рядок 13). А з iOS 18.4 декларативний push дає серверу самому описати сповіщення, тож воно з’являється, навіть якщо воркер так і не запуститься. Лічильники працюють за тим самим принципом (рядок 9): встановлений застосунок може поставити число на своїй іконці, і система показує його разом з іншими сповіщеннями застосунку.

PWA, обгортка чи застосунок: питання магазинів

Перевага PWA, що переживає будь-яке порівняння, — дистрибуція. Немає черги рев’ю, акаунта в магазині, сертифіката, який треба поновлювати, і збірки, яку треба підписувати. Виправлення виходить із наступним деплоєм, а посилання — це весь процес встановлення. Для продукту, який знаходять пошуком або отримують посиланням, це різниця між «спробувати зараз» і «спершу щось встановити». Чого посилання не дає — це картки в магазині, де люди гортають застосунки, платіжної системи магазину й довіри, яку частина користувачів має лише до застосунків із магазину. Є чотири способи повернути частину цього, і в кожного своя пастка:

ШляхЩо постачаєтьсяОновленняПастка
Лише PWAСайтКожен деплойНемає картки в магазині; на iPhone користувачів треба навчити встановлювати (рядок 10)
Google Play як Trusted Web ActivityТонкий Android-застосунок, що відкриває ваш сайт у Chrome на весь екран (збирає Bubblewrap)Вебчастина — з кожним деплоєм; оболонка — рідкоDigital Asset Links мають довести, що сайт ваш, інакше з’являється адресний рядок браузера; діють правила Play, зокрема щодо оплати цифрових товарів
Microsoft StorePWA, запакована через PWABuilderКожен деплойОхоплює лише Windows
Вебкод у нативній оболонці (Capacitor, Cordova)Ваш вебкод, упакований у застосунок, що показує його у WebView, із плагінами для нативних можливостейЧерез рев’ю магазинів; вебкод можна оновлювати «по повітрю» в межах правил магазинівApple відхиляє застосунок, що є лише «перепакованим сайтом» (пункт 4.2); тепер ви ще й підтримуєте застосунок

Пункт 4.2 правил Apple — та лінія, що вирішує останній рядок: застосунок має містити можливості, вміст та інтерфейс, що «підносять його над перепакованим сайтом», а той, що не корисний, не унікальний і не схожий на застосунок, у магазині не місце. Оболонка навколо вашого сайту без нічого нативного всередині — саме те, що це правило відхиляє. На Android навпаки: Trusted Web Activity — підтримуваний спосіб покласти сайт у Google Play, якщо Digital Asset Links на вашому сайті доводять, що застосунок і сайт належать одному розробникові. Саме рішення випливає з таблиці можливостей більше, ніж зі смаку:

Якщо продукт…Будуйте
Потребує лише рядків 1–7, і його знаходять пошуком чи посиланнями — контент, комерція, бронювання, дашборди, внутрішні інструментиPWA
Потребує рядків 8–11 на iPhone — push, лічильниківPWA зі спроєктованим кроком встановлення — або застосунок, якщо користувачі не встановлюватимуть
Потребує картки в Google Play, але нічого поза вебомPWA плюс Trusted Web Activity
Потребує рядків 12–16 на iPhone або роботи, коли застосунок закритоНативний або кросплатформний застосунок — див. сторінки про React Native, Flutter, iOS та Android
Є грою, камерою, медіазастосунком чи будь-чим, де важить кожен кадрЗастосунок

П’ять кейсів і те, чого в кожному бракувало

Три з п’яти кейсів — про платформу під вебзастосунком: виробник браузера чи операційна система змінює правило, — а два — про вибір самої команди. Кожен задокументували ті, кого він стосувався.

КейсЩо сталосяЧого бракувало
Create React App, 2017–2018Найпопулярніший стартовий шаблон React зробив кожен новий застосунок типово офлайн-першим; за півтора року service worker зробили необов’язковимПроцесу оновлення: service worker — це рішення про деплой, а не налаштування шаблону
Сім днів Safari, березень 2020Safari почав видаляти все, що зберегли скрипти сайту, після семи днів користування без візитуСервера зі справжньою копією; чернетки й офлайн-дані, що жили лише в браузері, пропали
Pinterest, 2017–2018Після ставки лише на нативні застосунки компанія за три місяці перебудувала мобільний сайт як PWA; реєстрації за рік зросли на 843%До того: швидкого мобільного вебу для людей, що приходять за посиланням
Apple і ЄС, лют.–бер. 2024Бета-версії iOS 17.4 перетворили застосунки на домашньому екрані в ЄС на ярлики Safari; 1 березня Apple це скасувалаПлану на випадок, коли платформа вимикає режим встановлення, а з ним push і лічильники
Запити на сповіщення, 2020–2025Chrome приглушив запити для сайтів із низьким рівнем згоди, а потім почав забирати невикористані дозволи — встановлені вебзастосунки винятокЗапиту в мить, коли користувачеві щось потрібно, а не під час першого візиту

Перший кейс показує, як поширюється проблема оновлення. Create React App 1.0, що вийшов 18 травня 2017 року, зробив кожен новий проєкт типово прогресивним вебзастосунком: service worker кешував усю збірку й віддавав її за принципом «спершу кеш». Через десять днів з’явився issue під назвою «Why was service worker merged into create react app?», і в понад 154 коментарях розробники описували те саме. Вони деплоїли, а їхні користувачі далі бачили старий сайт. Версія 2.0 від 1 жовтня 2018 року зробила service worker необов’язковим. Документація прямо назвала причину: офлайн-перші застосунки можуть ускладнити налагодження деплоїв, а користувачі бачать старіший вміст, доки не закриють усі відкриті вкладки, — «перезавантаження замало». У воркері нічого не було зламано. Бракувало процесу оновлення з розділу вище, а шаблон не може знати, коли вашим користувачам слід отримати нову версію.

Інші три кейси — про правила, якими команда не володіє. У березні 2020 року Safari почав видаляти все сховище сайту, записане скриптами, — IndexedDB, localStorage, реєстрації service worker і кеші — після семи днів користування Safari без візиту. Це було спрямовано проти трекерів, і застосунки на домашньому екрані стали винятком, але будь-який вебзастосунок, що тримав єдину копію чогось користувацького в браузері, її втратив. У лютому 2024 року бета-версії iOS 17.4 перетворили вебзастосунки на домашньому екрані в ЄС на ярлики, що відкривалися у вкладці Safari, — без власного вікна, push і лічильників; Apple послалася на Акт про цифрові ринки ЄС. Після хвилі обурення розробників і на тлі уваги Європейської комісії Apple скасувала зміну 1 березня. А браузери поступово зачиняють двері для недбалого push: Chrome 80 2020 року приглушив запит для сайтів, чиї запити користувачі зазвичай відхиляють, а з жовтня 2025 року Chrome забирає дозвіл на сповіщення в сайтів, які користувач більше не відвідує, зазначивши, що взаємодію отримує менше 1% сповіщень. Встановлені вебзастосунки на друге правило не підпадають. Усі три кажуть одне: вебзастосунок працює за правилами, які встановлює хтось інший, тож він тримає дані на власному сервері, має другий канал для всього критичного й заробляє кожен дозвіл у мить, коли той потрібен.

Де це вироджується

PWA відмовляє так, що з машини розробника цього не видно: там швидка мережа, браузер — Chrome, а вкладка завжди свіжа. Майже кожен збій — одне з цього:

  • Немає процесу оновлення — випущене виправлення чекає за старим воркером, доки відкрита вкладка чи застосунок на домашньому екрані.
  • Перейменований файл service worker, через який зламаний лишається зареєстрованим і далі відповідає.
  • index.html без хешу, який віддають за принципом «спершу кеш», закріплюючи користувачів на одному релізі.
  • Дані API з кешу, показані як актуальні, без часу отримання.
  • Офлайн-записи, які повторюють без ключа ідемпотентності, — і одне замовлення стає двома.
  • Єдина копія даних користувача у сховищі браузера, яке браузер має право видалити (рядок 2).
  • Закешовані непрозорі відповіді, що ховають помилки й з’їдають квоту по сім мегабайтів за раз.
  • Запит на сповіщення під час першого візиту — один раз відхилений і приглушений назавжди.
  • Фіча, збудована на рядках 8–16, відсутність якої на iPhone виявили після затвердження дизайну.
  • Обгортка для магазину без нічого нативного, відхилена за пунктом 4.2.

Чесна межа в тому, що PWA живе всередині браузера, а браузер належить комусь іншому. На Android і десктопі Chromium дає вебзастосунку майже все, що отримує застосунок, і розрив далі скорочується. На iPhone веб отримує те, що вирішить Apple, рішення встигало змінитися в обидва боки за один цикл бета-версій, а рушій під ним ви не обираєте. Це причина будувати PWA з розплющеними очима, а не причина її уникати. Для більшості продуктів, що живуть із того, що їх знаходять і поширюють, посилання, яке працює одразу, краще за застосунок, який спершу треба встановити, — якщо нічого суттєвого не залежить від можливості, якої одна з платформ не дає.

Практична перевірка займає годину. Випишіть кожну можливість, потрібну продукту, і знайдіть її рядок у таблиці вгорі. Відкрийте сайт на iPhone, додайте його на домашній екран і перевірте, що там працюють вхід, push і офлайн. Задеплойте помітну зміну, лишивши вкладку відкритою, і подивіться, коли вона доїде. Знайдіть назву файлу service worker і переконайтеся, що вже сьогодні за цією адресою можна задеплоїти заміну, яка нічого не робить. Пошукайте в правилах кешування щось персональне або щось непрозоре. Вимкніть мережу, зробіть зміну, двічі ввімкніть зв’язок і порахуйте, скільки разів її отримав сервер.

Коли це застосовувати

Застосовуйте, коли

  • Продукти, які по суті є сайтом і виграють від офлайну та місця на домашньому екрані.
  • Усе, що знаходять пошуком або передають посиланням, де крок встановлення коштує вам користувачів.
  • Внутрішні інструменти й польові застосунки на ваших пристроях, де рев’ю в магазині — чисті накладні витрати.
  • Одна кодова база на десктоп і мобільні від команди, яка не може забезпечити людьми дві нативні платформи.

Уникайте, коли

  • Усе, що потребує Bluetooth, NFC, USB чи послідовного порту на iPhone (рядок 15).
  • Продукти, що залежать від роботи, поки застосунок закрито (рядки 12–14).
  • Продукти, чиї користувачі шукають застосунки, гортаючи магазин, — туди посилання не доходить.
  • Ігри, камера, медіазастосунки й усе інше, де браузер — хибне середовище для кожного кадру.

Джерела й метод

На чому стоїть ця стаття

  • 17 джерел
  • 1 задокументований збій
  • Перевірено 13 жовтня 2026 р.

Дослідження з Claude. Пошук і звірку першоджерел зроблено з Claude (Anthropic). Відбір, текст і перевірку фактів — редакція Anatomy of IT Product.

Було корисно?

Поділіться з тим, хто працює над тією ж задачею.