
Что такое PWA, как они работают и как создать PWA
PWA (Progressive Web App, прогрессивное веб-приложение) — это сайт, который использует стандартные веб-технологии для установки на устройство, запуска в отдельном окне, офлайн-работы и других возможностей приложения.

PWA (Progressive Web App, прогрессивное веб-приложение) — это сайт, который использует стандартные веб-технологии для установки на устройство, запуска в отдельном окне, офлайн-работы и других возможностей приложения. Для управляемой установки обычно нужны HTTPS и файл manifest, а для офлайн-режима — service worker с продуманным кешированием. Ни manifest, ни кнопка «Установить» сами по себе не сделают сайт полезным без интернета: доступные офлайн-экраны, данные, обновления и восстановление после очистки хранилища нужно проектировать отдельно.
Что такое PWA и когда она нужна
PWA остаётся веб-приложением: его интерфейс написан на HTML, CSS и JavaScript, страницы доступны по URL, а новая версия размещается на сервере. Совместимый браузер может добавить приложение на домашний экран, в меню «Пуск», Dock или панель задач. После запуска оно выглядит как отдельная программа, если выбран режим standalone.
Слово Progressive означает постепенное улучшение. Базовый сайт должен продолжать выполнять основную задачу в браузере. Установка, офлайн-кеш, push-уведомления и интеграция с операционной системой подключаются там, где их поддерживает устройство.
PWA особенно полезна в следующих случаях:
- у сервиса уже есть адаптивная веб-версия, и команда хочет развивать одну кодовую базу;
- пользователи часто возвращаются в личный кабинет, каталог, редактор, программу лояльности или внутреннюю систему;
- часть интерфейса и ранее открытые данные должны загружаться при нестабильной сети;
- продукту важна установка прямо с сайта без обязательного перехода в магазин приложений;
- нужны web push-уведомления, но команда готова учитывать ограничения каждой платформы.
Нативное или гибридное приложение может лучше подойти, если продукт зависит от непрерывной фоновой работы, специфичных системных API, сложной обработки медиа или обязательного присутствия в магазинах. PWA и нативный клиент могут существовать одновременно: веб-приложение закрывает широкий сценарий, а платформенный клиент — функции, которых нет в нужном браузере.
Как устроена PWA
У PWA нет единственного обязательного программного каркаса. React, Vue, Angular и серверный рендеринг допустимы, но не определяют саму технологию. Практический минимум складывается из нескольких независимых частей.
Web App Manifest
Manifest — JSON-файл с метаданными установленного приложения. В нём задают:
- полное и короткое название;
- стабильный идентификатор
id; - стартовый URL и область приложения
scope; - режим отображения, например
standalone; - цвета интерфейса;
- иконки обычного и maskable-типа.
Manifest помогает браузеру показать корректное имя и иконку, открыть нужный адрес и отделить окно приложения от вкладки. Он не управляет сетевыми запросами и не создаёт офлайн-режим.
Service worker и HTTPS
Service worker — JavaScript-файл, который браузер запускает отдельно от страницы. Он может перехватывать запросы в своей области действия, отвечать данными из Cache API, получать push-события и выполнять поддерживаемые фоновые задачи. Прямого доступа к DOM страницы у него нет.
Service worker работает в безопасном контексте. На рабочем сайте нужен HTTPS; исключение для разработки — localhost и 127.0.0.1. Положение файла влияет на область действия: /sw.js по умолчанию контролирует корень сайта, а /assets/sw.js — только /assets/ и вложенные адреса, если сервер не расширил scope специальным заголовком.
Жизненный цикл состоит из установки, ожидания и активации. При обновлении новая версия worker обычно устанавливается рядом с действующей, затем ждёт, пока старая перестанет контролировать открытые вкладки. Благодаря этому интерфейс и worker одной версии не смешиваются без решения разработчика.
Cache API, IndexedDB и сервер
Cache API хранит пары Request/Response: HTML, стили, скрипты, изображения и ответы GET-запросов. IndexedDB подходит для структурированных данных — черновиков, очереди несинхронизированных действий и ранее полученных записей. localStorage внутри service worker недоступен.
Браузерное хранилище имеет квоты. Его может очистить пользователь, а при нехватке места — браузер. Поэтому заказ, документ или другое критическое изменение нельзя считать сохранённым, пока сервер не подтвердил запись. Локальная копия ускоряет работу и помогает пережить обрыв сети, но серверная синхронизация остаётся частью архитектуры.
Что реально поддерживают браузеры в 2026 году
У PWA нет единого статуса Baseline, потому что это сочетание функций. Service Worker, Cache API и Push API относятся к Baseline Widely available, но правила установки, разрешения и системная интеграция различаются. beforeinstallprompt, Background Sync и Periodic Background Sync имеют Limited availability и не подходят как единственный путь к важной функции.
Актуальные условия installability на MDN также разделяют установку и офлайн-режим: service worker широко используется для offline, но не является универсальным условием добавления сайта как приложения.
| Платформа | Как устанавливают | Что учесть |
|---|---|---|
| Android | Через команду Install или «Добавить на главный экран» в браузере | Результатом может быть WebAPK либо ярлык — это зависит от браузера, устройства и соответствия критериям. Проверяйте именно целевые браузеры. |
| iOS и iPadOS 26 | Share → Add to Home Screen; параметр Open as Web App включён по умолчанию | Safari 26 позволяет добавить любой сайт как web app. Manifest по-прежнему нужен для контролируемых иконок, имени и запуска. Автоматического install prompt на iOS нет. |
| Windows, Linux, macOS в Chromium | Значок установки в адресной строке или команда в меню браузера | Установленная PWA получает отдельное окно и системный ярлык. Доступ к дополнительным API зависит от браузера и ОС. |
| macOS в Safari 17+ | File → Add to Dock | Safari может добавить в Dock сайт без manifest; manifest всё равно улучшает представление приложения. |
| Firefox на компьютере | Штатной установки PWA по manifest нет | Сайт продолжит работать в браузере, а поддерживаемые service worker-функции можно использовать без установки. Firefox на Android поддерживает добавление веб-приложений. |
Проверка только по названию браузера ненадёжна. Используйте feature detection, например "serviceWorker" in navigator, и сохраняйте обычный сетевой путь для неподдерживаемых функций.
Какой способ создания выбрать
| Способ | Когда выбирать | Что он экономит | Что остаётся сделать |
|---|---|---|---|
| Вручную: manifest и собственный service worker | Небольшой сайт, обучение, особые правила offline | Нет лишней зависимости, полный контроль | Самостоятельно реализовать стратегии кеша, обновления, ошибки и тест-матрицу |
| Workbox или PWA-плагин фреймворка/сборщика | Проект уже собирает ассеты с хешами, нужен precache и типовые стратегии | Генерацию списка файлов, маршрутизацию кеша и часть lifecycle-кода | Настроить API, пользовательские данные, обновление интерфейса и платформенные проверки |
| CMS-плагин или PWABuilder | Нужна базовая установка существующего сайта либо заготовка пакета для магазина | Создание manifest, иконок, простого worker и упаковки | Проверить качество исходного сайта, scope, offline, авторизацию, push и требования магазина |
В Workbox есть два характерных пути. generateSW создаёт готовый worker для простого precache и настраиваемого runtime cache. injectManifest добавляет список сборочных файлов в собственный worker и оставляет разработчику сложную маршрутизацию, push и другую логику.
Плагин CMS заметно сокращает стартовую работу для блога или каталога. Он не знает, можно ли показывать устаревшую цену, как повторно отправить офлайн-заказ и что делать при конфликте черновиков. Такие правила задаёт владелец продукта.
PWABuilder полезен для аудита исходного сайта, генерации основы и подготовки store-пакета. Публикация через Trusted Web Activity, WebView-обёртку или другой контейнер является отдельным проектом: магазин проверяет пакет и соблюдение своих правил, а не только наличие manifest.
Как создать PWA вручную
Ниже — минимальный вариант для обычного адаптивного сайта. Он показывает архитектуру и даёт рабочий офлайн-fallback. Перед внедрением решите, какие URL можно кешировать и какие данные должны всегда приходить с сервера.
Подготовьте сайт и иконки
Проверьте, что интерфейс удобен с сенсорным экраном и клавиатурой, каждая важная страница открывается по собственному URL, а рабочий домен использует HTTPS без смешанного HTTP-контента.
Подготовьте PNG-иконки 192×192 и 512×512 пикселей. Для Android полезна отдельная maskable-иконка: её важная часть должна помещаться в безопасную центральную область, чтобы система могла обрезать изображение под круг или другую форму.
Создайте manifest
Сохраните файл /manifest.webmanifest:
{
"name": "Мой сервис",
"short_name": "Сервис",
"id": "/",
"start_url": "/",
"scope": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0b57d0",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icons/icon-512-maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
Значения start_url и scope должны попадать в доступную область сайта. Стабильный id помогает браузеру узнавать приложение после изменения стартового URL.
Подключите manifest и зарегистрируйте worker
Добавьте в <head> каждой точки входа:
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#0b57d0">
<link rel="apple-touch-icon" href="/icons/icon-192.png">
Зарегистрируйте worker в клиентском JavaScript:
if ("serviceWorker" in navigator) {
window.addEventListener("load", async () => {
try {
const registration = await navigator.serviceWorker.register("/sw.js", {
scope: "/"
});
console.log("Service worker scope:", registration.scope);
} catch (error) {
console.error("Service worker registration failed:", error);
}
});
}
Проверка поддержки сохраняет работу сайта в браузере без Service Worker API. Абсолютный путь /sw.js помогает охватить весь сайт.
Добавьте минимальный service worker
Создайте /sw.js:
const CACHE_PREFIX = "my-service-";
const CACHE_NAME = `${CACHE_PREFIX}shell-v1`;
const PRECACHE = [
"/",
"/offline.html",
"/styles.css",
"/app.js",
"/icons/icon-192.png"
];
const STATIC_TYPES = new Set(["style", "script", "image", "font"]);
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE))
);
});
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((names) =>
Promise.all(
names
.filter((name) =>
name.startsWith(CACHE_PREFIX) && name !== CACHE_NAME
)
.map((name) => caches.delete(name))
)
)
);
});
async function networkFirst(request) {
try {
const response = await fetch(request);
if (response.ok) {
const cache = await caches.open(CACHE_NAME);
await cache.put(request, response.clone());
}
return response;
} catch {
return (await caches.match(request)) || caches.match("/offline.html");
}
}
async function cacheFirst(request) {
const cached = await caches.match(request);
if (cached) return cached;
const response = await fetch(request);
if (response.ok) {
const cache = await caches.open(CACHE_NAME);
await cache.put(request, response.clone());
}
return response;
}
self.addEventListener("fetch", (event) => {
const request = event.request;
if (request.method !== "GET") return;
if (request.mode === "navigate") {
event.respondWith(networkFirst(request));
return;
}
const sameOrigin = new URL(request.url).origin === self.location.origin;
if (sameOrigin && STATIC_TYPES.has(request.destination)) {
event.respondWith(cacheFirst(request));
}
});
Этот пример рассчитан на публичные страницы: он сначала запрашивает навигацию по сети, сохраняет успешный ответ и при сбое возвращает ранее открытую страницу либо /offline.html. Статические ресурсы своего домена обслуживаются из кеша. POST-запросы и API-ответы намеренно не кешируются. В приложении с авторизацией ограничьте кешируемые маршруты явным списком и не сохраняйте персональный HTML общим правилом.
Создайте простую самодостаточную /offline.html. В ней должны быть понятное сообщение, кнопка повторной попытки и только те стили или иконки, которые входят в precache. Если хотя бы один URL из PRECACHE отвечает ошибкой, cache.addAll() отклонится и новая версия worker не установится.
Настройте ответы сервера
Проверьте четыре условия:
/manifest.webmanifest,/sw.jsи все иконки отвечают без перенаправлений и ошибок;- сервер отдаёт manifest как
application/manifest+jsonили совместимый JSON-тип, а worker — как JavaScript; sw.jsне получает многомесячный immutable-кеш; практичный вариант —Cache-Control: no-cache, чтобы браузер мог быстро сверять версию;- файлы с хешем в имени можно кешировать надолго, а HTML и данные — по правилам их актуальности.
При изменении app shell увеличьте версию my-service-shell-v1. Обработчик activate удалит только кеши этого приложения прошлых версий после активации нового worker.
Как сделать офлайн-режим полезным
«Работает офлайн» должно обозначать конкретный сценарий. Например: приложение запускается, показывает ранее открытые статьи, сохраняет черновик и сообщает, что отправит его после возвращения сети. Полный каталог, свежие остатки и оплата могут оставаться сетевыми функциями.
Выбирайте стратегию по типу ресурса:
- Precache — оболочка интерфейса, offline-страница, базовые стили и код текущей версии.
- Cache first — файлы с хешированными именами, шрифты и неизменяемые изображения.
- Stale while revalidate — некритичные аватары и справочные данные, где быстрый старый ответ допустим, а обновление можно скачать в фоне.
- Network first — ранее открытые документы или лента, если важна свежесть, но при сбое допустима копия с отметкой времени.
- Network only — авторизация, оплата, остатки, персональные права и операции, для которых устаревший ответ опаснее отсутствия ответа.
Для офлайн-форм сохраняйте черновик и очередь в IndexedDB. Каждой операции назначайте устойчивый идентификатор, а сервер делайте идемпотентным: повторная отправка после обрыва не должна создавать второй заказ. Если Background Sync недоступен, отправляйте очередь при следующем открытии приложения или событии online и явно показывайте статус пользователю.
Не кешируйте по общему правилу все GET-ответы. В кеше могут оказаться персональные данные, ответы с ошибками или информация одного аккаунта после выхода из системы. Для приватных данных нужны очистка при logout, ограничение времени жизни и проверка пользователя на сервере.
Как работают установка, push и обновления
Установка
В Chromium можно показать собственную кнопку установки через событие beforeinstallprompt. Кнопка должна быть скрыта, пока событие не получено. Этот API не является Baseline и не работает на iOS, поэтому рядом нужен обычный путь через меню браузера или короткая инструкция Share → Add to Home Screen.
Установку предлагайте после того, как человек понял пользу приложения: например, после сохранения первого документа или повторного визита. Немедленный баннер на первой странице конкурирует с основным действием и не объясняет, зачем добавлять сайт на устройство.
Push-уведомления
Manifest не включает push автоматически. Для классического Web Push нужны service worker, подписка браузера, сервер отправки и обработчик клика по уведомлению. Разрешение запрашивайте после явного действия — кнопки «Включить уведомления» с описанием их типа и частоты.
На iOS и iPadOS Web Push поддерживается с версии 16.4 для веб-приложений, добавленных на Home Screen. В обычной вкладке тот же сценарий рассчитывать на push не должен. На каждой платформе проверяйте PushManager, Notifications API и фактическое состояние разрешения.
Обновления
Браузер периодически сравнивает файл worker с серверной версией. Если код изменился, он устанавливает новую версию в фоне. Действующий worker продолжает обслуживать открытые вкладки, а новый может оставаться в waiting до их закрытия.
Для небольшого сайта достаточно сообщить «Доступна новая версия» и предложить перезагрузку после активации. В сложном приложении согласуйте версии HTML, API и кеша. Безусловный skipWaiting() ускоряет активацию, но новый worker способен начать обслуживать старую открытую страницу. Используйте его только тогда, когда версии совместимы или интерфейс контролируемо перезагружается.
Изменение иконки или названия в manifest может применяться не сразу и зависит от платформы. Иногда пользователю потребуется переустановка. Не связывайте выпуск критического исправления только с обновлением метаданных установленного ярлыка.
Как проверить результат
Проверка начинается с чистого состояния. Старый worker и кеш часто скрывают ошибку, поэтому используйте новый профиль браузера либо очистите данные сайта перед первым проходом.
Manifest и область действия
В Chromium или Edge откройте DevTools → Application:
- в Manifest нет ошибок, видны правильные name, start URL, display и иконки;
- maskable-иконка сохраняет важную часть в безопасной области;
- service worker имеет ожидаемый статус и scope
/; - в Cache Storage находятся только запланированные ресурсы.
Актуальный Lighthouse больше не содержит отдельной категории PWA. Он полезен для производительности, доступности и общих проверок качества, но не заменяет Application и реальные устройства.
Офлайн и навигация
Сначала откройте приложение онлайн, дождитесь активации worker и перезагрузите страницу. Затем включите Offline в DevTools или отключите сеть и проверьте:
- запуск с установленной иконки;
- перезагрузку главной и ранее открытой вложенной страницы;
- прямой переход по deep link;
- отображение offline-страницы для неизвестного маршрута;
- поведение изображений, шрифтов и API;
- понятный статус несохранённой формы и повторную отправку после возврата сети.
Обновление
Оставьте старую вкладку открытой, опубликуйте тестовую версию worker с новым именем кеша и откройте вторую вкладку. Убедитесь, что новая версия появляется в waiting, старый интерфейс продолжает работать, а после выбранного действия или закрытия вкладок активируется согласованный набор файлов. Отдельно проверьте, что старые кеши удалились и обновление не требует двух случайных перезагрузок.
Установка и восстановление
Минимальная матрица — Android в основном браузере аудитории, iPhone/iPad через Home Screen и компьютерный Chromium. Если важны macOS или Firefox, добавьте Safari Add to Dock и обычную браузерную версию Firefox.
После успешного сценария вручную очистите данные сайта. Приложение должно корректно войти заново, скачать серверные записи и объяснить потерю несинхронизированного локального черновика. Такая проверка не воспроизводит все алгоритмы eviction, но подтверждает, что локальное хранилище не стало единственной копией данных.
Push проверяйте только если он реализован: разрешение после клика, отказ пользователя, получение в фоне, переход по уведомлению на правильный URL и повторную подписку после очистки данных. На iOS выполняйте тест из установленной Home Screen web app.
Типичные ошибки
- Установка принята за готовую PWA. Иконка и отдельное окно не подтверждают offline, обновления и восстановление данных.
- Worker лежит слишком глубоко. Файл в
/assets/не контролирует маршруты корня с областью действия по умолчанию. - В precache указан несуществующий файл. Один ответ 404 срывает
cache.addAll()и установку новой версии worker. - Одна стратегия применена ко всем запросам. Cache first показывает старые цены и права, а network only лишает приложение офлайн-оболочки.
- Старые кеши не версионируются. Пользователь получает несовместимые HTML и JavaScript или долго видит прошлый релиз.
skipWaiting()включён без сценария обновления. Новый worker начинает отвечать старой открытой странице.- Push запрашивается на первом экране. Пользователь не понимает ценность, запрещает уведомления, а повторный запрос блокируется настройками браузера.
- Локальные данные считаются постоянными. Очистка сайта, приватный режим, квота или нехватка места удаляют кеш и IndexedDB.
- Проверяется только localhost. На рабочем домене проявляются ошибки HTTPS, MIME-типа, редиректов, заголовков кеша и scope.
- Тест ограничен Lighthouse. Нужны Application, чистое состояние и реальные сценарии на целевых устройствах.
Автор статьи

Редактор и автор статей
Пишет экспертные материалы о цифровом маркетинге и автоматизации. Журналист с опытом в деловых медиа, отвечает за качество и достоверность публикаций.
Вопросы и ответы
Почти любой современный адаптивный сайт можно сделать устанавливаемым, добавив manifest и корректную серверную настройку. Полезная PWA потребует больше работы: нужно определить офлайн-сценарии, кеш, обновления, авторизацию и восстановление данных. Генератор не исправит неудобный мобильный интерфейс и не создаст бизнес-логику автоматически.
Не во всех браузерах. Safari 26 на iOS/iPadOS и Safari на macOS позволяют добавить любой сайт как web app, а некоторые Chromium-сценарии также устанавливают сайт без прежнего набора критериев. Service worker нужен для управляемого офлайн-режима, классического Web Push и других фоновых возможностей.
Да. В iOS/iPadOS 26 пользователь выбирает Share → Add to Home Screen, после чего сайт по умолчанию открывается как web app. Автоматического диалога установки, управляемого через beforeinstallprompt, нет. Web Push работает для Home Screen web apps начиная с iOS/iPadOS 16.4 и требует разрешения после действия пользователя.
Может, если все нужные ресурсы и данные заранее сохранены, а операции не требуют сервера. Для магазина, банка или совместного редактора разумнее частичный offline: оболочка, ранее открытые данные и черновики доступны, а оплата, свежие остатки и подтверждение изменений ждут сеть.
Да, через платформенные пакеты и обёртки: например, Trusted Web Activity на Android или создаваемый инструментом пакет для Microsoft Store. Для App Store применяется отдельная оболочка и правила Apple. В каждом случае нужно пройти требования и проверку магазина; веб-установка с сайта от этого не зависит.
Наличие manifest и service worker само по себе не повышает позиции. Поисковику по-прежнему нужны доступные URL, индексируемый контент, корректные ответы сервера, скорость и внутренняя перелинковка. Service worker не должен маскировать серверные ошибки одинаковым ответом 200 для всех адресов.
Код и данные загружаются с сайта, а браузер проверяет обновления service worker. Новая версия может ждать закрытия вкладок. Приложению нужен понятный сценарий: фоновая установка, сообщение о готовом обновлении и безопасная перезагрузка. Метаданные manifest, включая иконку, обновляются по правилам конкретной платформы и иногда требуют переустановки.
Единого гарантированного объёма нет. Квота зависит от браузера, устройства, свободного места и активности пользователя. navigator.storage.estimate() показывает оценку, а navigator.storage.persist() может запросить постоянный режим, но результат не гарантирован. Критические данные синхронизируйте с сервером и обрабатывайте QuotaExceededError.




Комментарии(0)
Оставьте комментарий
Войдите, чтобы присоединиться к обсуждению