Установка PWA (progressive web app, прогрессивное веб-приложение) ломается по трём слоям причин: страница не проходит критерии устанавливаемости, платформа не умеет показывать кнопку автоматически, либо конкретное окружение вообще блокирует установку. На iPhone штатной кнопки «Установить» не существует — приложение добавляют через меню «Поделиться» в Safari. На Android и в десктопном Chrome кнопка появляется только при валидном манифесте и зарегистрированном service worker. Диагностику ведут от симптома к причине: сначала технические критерии, потом поведение браузера, потом кэш и профиль.
Диагностика PWA за пять шагов: от симптома к причине
Порядок важен: симптомы «нет кнопки», «кнопка есть, но не работает» и «ставится, но криво открывается» ведут к разным поломкам. Начнёте с последнего — легко час крутить иконки, когда проблема в отсутствии service worker.
Шаг 1. Проверить базовые критерии устанавливаемости
Браузер считает приложение устанавливаемым, когда сходится набор условий. Сайт отдаётся по HTTPS (исключение — localhost при разработке). Есть файл манифеста, подключённый через <link rel="manifest">, и он валиден как JSON. В манифесте заполнены name или short_name, набор иконок с размерами, start_url и display со значением standalone либо fullscreen. Зарегистрирован service worker с обработчиком fetch, и он перешёл в состояние activated.
Отсутствие любого элемента рушит всю цепочку. Частая история: манифест лежит рядом, но отдаётся с неверным MIME-типом, и браузер его молча игнорирует. Ещё одна — start_url указывает на другой поддомен или на страницу вне scope, из-за чего установка блокируется. Что именно требуется от манифеста и как собрать его руками, разобрано в материале как сделать PWA-приложение из сайта; там же логика сборки, если вы собираете приложение из готового лендинга.
Шаг 2. Открыть DevTools → Application
В Chrome и Chromium-браузерах вкладка Application показывает всё нужное сразу. Раздел Manifest перечисляет распознанные поля и подсвечивает проблемы: битый JSON, пустые name, icons, start_url, предупреждения о размерах иконок. Раздел Service Workers показывает статус регистрации, версию и кнопки Update / Unregister. Cache Storage подсказывает, лежит ли в кэше старая копия страницы.
Пройдитесь по трём разделам сверху вниз и запишите всё, что подсвечено красным или жёлтым. В Manifest пусто — проблема в подключении файла или в MIME. Манифест читается, а Service Workers показывает redundant или ничего — копайте регистрацию: скрипт мог не загрузиться из-за пути, ошибки в самом файле или CSP-заголовков.
Шаг 3. Посмотреть Installability и аудит Lighthouse
В той же панели Application у манифеста есть блок с критериями устанавливаемости: он прямо перечисляет, какие пункты пройдены, а какие нет. Это самый быстрый способ увидеть вердикт браузера без догадок. В Lighthouse за это отвечает категория PWA — список замечаний с привязкой к исходникам.
Аудит стоит воспринимать как чек-лист, а не как приговор. Часть рекомендаций касается удобства и офлайн-режима, и на саму возможность установки они не влияют. «Не зарегистрирован service worker» и «service worker не отвечает офлайн» — разные по последствиям пункты.
Шаг 4. Воспроизвести на чистом профиле
Кэш, ранее отклонённый промпт и уже установленная копия маскируют реальное состояние. Chrome запоминает отказ пользователя от установки и какое-то время не показывает баннер повторно. Service worker может отдавать старую версию манифеста. Установленное приложение конфликтует с новой попыткой.
Проверяйте в новом профиле браузера, в режиме инкогнито (там service worker живёт отдельно) и на втором устройстве. В чистом профиле всё ставится — дело в локальном состоянии, пользователю достаточно очистить данные сайта. Не ставится и там — проблема на стороне кода или сервера.
Шаг 5. Сверить с платформенной матрицей
Одинакового поведения не существует. Сверьте симптом со сводкой, прежде чем лезть в код.
| Окружение | Автоматический баннер установки | Как ставит пользователь |
|---|---|---|
| Chrome, Edge, Opera на Android | Да, при выполнении критериев | Кнопка в промпте или меню «Установить приложение» |
| Chrome, Edge на десктопе | Да, иконка в адресной строке | Иконка установки или меню браузера |
| Safari на iPhone и iPad | Нет, события с промптом не существует | Меню «Поделиться» → «На экран „Домой“» |
| Firefox на Android | Ограниченно, зависит от версии | Через меню браузера |
| Встроенный WebView в приложениях | Нет | Установка недоступна, нужен переход в браузер |
Разница в поддержке — самая недооценённая причина жалоб «у меня не работает». Проверять поведение Chrome на Android и Safari на iOS как одно и то же — типичная ошибка при приёмке задач.
PWA не ставится на айфон: почему на iPhone нет кнопки «Установить»
В iOS и iPadOS нет события beforeinstallprompt. Сайт не может программно вызвать системное окно установки, поэтому любая кнопка «Установить приложение» внутри страницы на айфоне останется декоративной. Единственный рабочий путь — Safari: кнопка «Поделиться» в нижней панели, затем «На экран „Домой“». Пункт появляется не всегда, и вот от чего это зависит.
Открывать страницу нужно именно в Safari. Chrome, Firefox и прочие браузеры на iOS работают на системном движке WebKit и функции добавления на домашний экран не предоставляют. Пользователь, пришедший по ссылке из мессенджера или соцсети, видит встроенный браузер приложения — там пункта тоже не будет. Лечится подсказкой «откройте в Safari» и, по-хорошему, кнопкой копирования ссылки, потому что в WebView адресную строку не видно.
Второй блок причин — приватный режим и ограничения. В приватном окне Safari добавление на домашний экран не работает. Корпоративные профили, ограничения «Экранного времени» и родительский контроль могут отключать установку приложений. Стоит проверить и настройки контента: при жёстких ограничениях сайтов часть функций отваливается.
Третий слой — технический. Safari ориентируется на манифест и на иконку apple-touch-icon, а имя ярлыка берёт из short_name или <title>. Если манифест недоступен или в нём нет иконок, ярлык создастся со скриншотом страницы и кривым названием. Полный разбор платформенных нюансов, включая поведение пушевых уведомлений, лежит в материале PWA на iPhone.
Отдельно про запуск. Приложение, добавленное на домашний экран, открывается в отдельном окне, но проверять это надо через display-mode: standalone в CSS и navigator.standalone в JS. Иначе разработчик радуется «установилось», а по факту получился ярлык на вкладку Safari — при display: browser система так и делает.
Нет кнопки установки в Android и Chrome: где искать причину
Событие beforeinstallprompt не приходит, и баннер не показывается. Первое, что стоит исключить, — невыполненные критерии из первого раздела. Второе — страница открыта не на том origin: если манифест и start_url живут на разных поддоменах, браузер откажется. Третье — уже установленная копия: пока пользователь не удалит приложение, повторный промпт не появится.
Дальше идут менее очевидные вещи. Chrome не показывает установку для страниц, открытых в iframe, и для некоторых режимов встраивания. Промпт не сработает, если пользователь ранее нажал «Нет»: нужно подождать или сбросить состояние через настройки сайта. И совсем экзотика — политики браузера на управляемых устройствах, где установку блокирует администратор.
Проверить факт установки заранее позволяет метод getInstalledRelatedApps, хотя поддержка у него неравномерная. На практике надёжнее ориентироваться на appinstalled и медиазапрос display-mode, а кнопку установки делать условной: показывать её только тем, у кого событие реально пришло.
Не добавляется на экран «Домой»: что ломается чаще всего
Симптом «нажимаю, а на экране ничего не появляется» почти всегда означает одну из четырёх вещей. Действие выполнено не в том браузере. Страница открыта не по HTTPS. Система уже содержит ярлык с тем же адресом, и повторное добавление игнорируется — сначала удалите старый. Либо установка прошла, но пользователь ищет иконку не на первом экране, а в библиотеке приложений.
На iOS отдельная беда — кэш иконки: Safari запоминает apple-touch-icon и при смене картинки продолжает показывать старую. Помогает добавление версии к пути файла или переустановка ярлыка. На Android аналогично ведёт себя кэш service worker: обновлённый манифест подхватывается не сразу, а после обновления регистрации.
Проверено: 26.10.2026. Правила сторов и возможности платформ описаны на дату проверки; точные формулировки смотрите в официальной справке Apple, Google и MDN. Нашли неточность — напишите на redakciya@pwa-arbitrazh.com.