PWA из готового сайта делается без переписывания: нужен HTTPS, файл манифеста и зарегистрированный сервис-воркер. После этого браузер на Android и настольный Chrome показывает предложение установки, а страница открывается в отдельном окне без адресной строки. На iPhone автоматического баннера нет — приложение ставится вручную через «Поделиться» → «На экран “Домой”». Ниже — что должно быть готово в проекте до правок, какие поля манифеста реально влияют на установку, как проверить результат и где чаще всего всё ломается.
Что такое PWA из сайта и что должно быть готово до начала
PWA (Progressive Web App) — не отдельная технология и не «приложение на JavaScript», а набор требований к обычному сайту: защищённый протокол, манифест с описанием иконки и режима запуска, сервис-воркер как прослойка между страницей и сетью. Второго фронтенда не появляется — тот же HTML, тот же CSS, тот же бэкенд. Разбор терминов — в отдельном материале про то, что такое PWA-приложение простыми словами.
HTTPS и постоянный домен
Регистрация сервис-воркера разрешена только на защищённом origin (исключение — localhost для разработки). Пока сайт открывается по http, браузер не просто «не предложит установку» — он не даст зарегистрировать воркер и не покажет манифест в панели разработчика: воркер перехватывает все запросы страницы, и отдавать такой контроль незашифрованному соединению небезопасно. Сертификат, в том числе бесплатный, ставится до всех остальных работ.
Установленное приложение прибито к origin: домен, протокол и порт. После переезда на другой домен у пользователей останется иконка, открывающая старый адрес, а новое приложение придётся устанавливать заново. Поэтому start_url и сам хост продумывают сразу, а редиректы с www и с http на https настраивают так, чтобы не рвать область видимости воркера.
Третий момент — смешанный контент: http-ресурсы на https-странице браузер частично блокирует, и воркер падает на кэшировании. Проверять это стоит до добавления манифеста.
Адаптивная вёрстка и мобильный UX
В режиме display: standalone страница открывается в отдельном окне без адресной строки, кнопок «назад» и «обновить», поэтому вся навигация должна быть внутри интерфейса. Фиксированная шапка, меню-гамбургер, кнопка «на главную» — не украшение, а условие выхода из тупиковых экранов.
Дальше мелочи, которые вылезают только на телефоне: viewport без width=device-width даёт зум и «уменьшенный» интерфейс; 100vh в мобильных браузерах считается по видимой области и обрезает нижние кнопки; вырезы и «домашняя полоса» перекрывают контент, если не заданы safe-area отступы. Тап-зоны меньше удобного размера в приложении бесят сильнее, чем в браузере: адресной строки, куда можно попасть пальцем, больше нет.
Отдельно про формы: автозаполнение, inputmode, правильные типы полей и обработка появления клавиатуры. В standalone-окне клавиатура не сдвигает вьюпорт так же, как в браузере, и кнопка «Отправить» регулярно оказывается под ней.
Минимальный набор ассетов
До правок кода в проекте должно лежать:
- иконки в стандартных размерах — 192×192 и 512×512 пикселей (без них установка не считается возможной), плюс отдельная maskable-версия с запасом по краям, чтобы Android не обрезал логотип в кружок;
apple-touch-icon180×180 — Safari на iOS берёт иконку для домашнего экрана именно оттуда, а не из манифеста;- готовые
nameиshort_name— короткое имя обрезается под иконкой, длинный заголовок туда не влезает; - решение по
start_url— какой экран открывается при запуске с иконки; theme_colorиbackground_color— цвет статусной строки и фон заставки при старте.
Скриншоты приложения (screenshots в манифесте) не обязательны и на установку не влияют, но с ними диалог установки в Chrome выглядит аккуратнее.
Как сделать PWA-приложение из сайта: пошагово
Шаг 1. Манифест
Манифест — JSON-файл, обычно manifest.json в корне сайта, подключённый в <head> всех страниц (или хотя бы стартовой):
<link rel="manifest" href="/manifest.json">Минимальный рабочий набор полей:
{
"name": "Название проекта",
"short_name": "Проект",
"start_url": "/?utm_source=pwa",
"scope": "/",
"display": "standalone",
"background_color": "#101114",
"theme_color": "#101114",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}Разбор по полям, от важного к косметике:
| Поле | Зачем нужно | Влияет на установку |
|---|---|---|
name | полное имя в диалоге установки и на заставке | да |
short_name | подпись под иконкой на домашнем экране | да |
icons (192 и 512) | иконка приложения | да, без них установка недоступна |
start_url | адрес, который открывается при запуске | да, должен быть в пределах scope |
display | режим окна: standalone, fullscreen, minimal-ui, browser | да, browser фактически отменяет «приложение» |
scope | какие адреса считаются частью приложения | да, влияет на навигацию |
theme_color, background_color | цвет статусной строки и заставки | нет, оформление |
orientation, description, screenshots | ориентация, описание, превью в диалоге | нет, косметика |
Практическая хитрость: метка вида ?utm_source=pwa в start_url позволяет в аналитике отделять запуски с иконки от обычных заходов из браузера. Поле не обязательное, но потом экономит время при разборе трафика.
Шаг 2. Минимальный сервис-воркер
Сервис-воркер — отдельный JS-файл, который браузер запускает в фоне и который перехватывает сетевые запросы страницы. Для установки достаточно, чтобы он существовал, успешно регистрировался и имел обработчик события fetch. Кэширование добавляют позже: сначала лучше добиться, чтобы всё просто работало.
// /sw.js
self.addEventListener('install', () => {
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', () => {
// пока без кэширования: пустой обработчик нужен для installability
});Регистрация со страницы:
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js', { scope: '/' })
.catch((err) => console.error('SW failed', err));
});
}Ключевой нюанс — область видимости. Файл в корне контролирует всё, что ниже /; файл в /app/sw.js — только раздел /app/. Если положить воркер в подкаталог, а scope попросить шире, браузер откажет.
Со стратегиями кэширования не спешите: классическая ошибка новичка — сразу навесить cache-first на HTML. Страница начинает отдавать старую версию из кэша, пользователи не видят обновлений, а разработчик не понимает, почему правки «не доезжают». Разумный стартовый вариант — network-first для документов и cache-first для статики (иконки, шрифты, картинки). И отдельно заголовок Cache-Control: no-cache для самого sw.js на уровне сервера или CDN, иначе браузер может сутками не замечать новую версию воркера.
Шаг 3. Проверка установки в Chrome и Safari
В Chrome откройте DevTools → Application. Вкладка Manifest показывает, распознан ли файл, какие поля прочитаны и какие иконки не загрузились; вкладка Service Workers — статус регистрации и кнопку обновления. Затем Lighthouse и прогон по категории PWA: он прямо перечисляет, каких критериев не хватает.
Даже при полностью корректной настройке Chrome не показывает баннер установки при первом заходе: срабатывает эвристика вовлечённости, нужен какой-то прогресс по сайту и взаимодействие с ним. На настольном Chrome иконка установки появляется в адресной строке, в мобильном меню — пункт «Установить приложение». В Safari на macOS приложение добавляется через «Файл → Добавить в Dock».
На iOS всё иначе: Safari не показывает баннеров и не поддерживает автопредложение установки. Пользователь делает это руками — «Поделиться» → «На экран “Домой”». Поэтому в интерфейсе стоит заранее предусмотреть понятную подсказку об этом пути: иначе часть аудитории просто не узнает, что сайт можно поставить на домашний экран.
Проверено: 25.10.2026. Правила сторов и возможности платформ описаны на дату проверки; точные формулировки смотрите в официальной справке Apple, Google и MDN. Нашли неточность — напишите на redakciya@pwa-arbitrazh.com.