Страница может понять, работает ли она как установленное PWA или во вкладке: медиазапрос display-mode, navigator.standalone на iOS и что выдаёт контекст.
Откройте одно и то же веб-приложение с иконки на домашнем экране и из вкладки браузера — страница часто ведёт себя по-разному: нет адресной строки, есть собственное окно, иногда другая вёрстка. Страница не гадала. Она задала браузеру один вопрос: я запущена как установленное приложение или как обычная вкладка? Разберём, как работает эта проверка, где она ломается и почему она важна и для фингерпринтинга.
Главное
- Стандартная проверка — медиафункция
display-mode:window.matchMedia('(display-mode: standalone)').matchesравноtrue, когда страница работает в окне установленного приложения без интерфейса браузера. - Режим задаётся полем
displayманифеста веб-приложения, но страница видит тот режим, в котором её фактически запустили, а не тот, что запрошен в манифесте. - В iOS Safari есть нестандартное булево свойство
navigator.standalone. Это свойство Apple WebKit: на iOS оноtrueпри запуске с домашнего экрана,falseво вкладке иundefinedпочти везде в остальных случаях. - Надёжный код объединяет оба сигнала и считает результат подсказкой о способе отображения, а не доказательством личности или безопасности.
- Установленный контекст встречается намного реже вкладки, а user-agent, заявляющий iOS без
navigator.standalone, внутренне противоречив — поэтому инструменты детекции читают и эти сигналы.
Стандартный способ: медиазапрос display-mode
Манифест описывает внешний вид установленного приложения через поле display. Справочник MDN по display-mode перечисляет значения, с которыми может сопоставляться страница; основных четыре (есть и более новые режимы, например window-controls-overlay и picture-in-picture):
| Значение | Что видит пользователь |
|---|---|
fullscreen | Весь экран, без системного интерфейса |
standalone | Собственное окно приложения без адресной строки и вкладок браузера |
minimal-ui | Окно в стиле приложения с минимальным набором элементов браузера |
browser | Обычная вкладка или окно браузера |
Одна и та же медиафункция работает и в CSS, и в JavaScript:
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
// react if the user installs or switches while the page is open
window.matchMedia('(display-mode: standalone)')
.addEventListener('change', (e) => console.log('standalone:', e.matches));
@media (display-mode: standalone) {
.install-banner { display: none; }
}
Есть две тонкости. Во-первых, запрос показывает текущий режим: приложение, в манифесте которого указано standalone, но которое открыли по ссылке во вкладке, всё равно сообщит browser. Во-вторых, браузеры проходят по цепочке отката, определённой спецификацией, так что запрос fullscreen на платформе, где это запрещено, может закончиться standalone или minimal-ui. Проверяйте нужный вам режим, а не значение из манифеста.
Дополнение для iOS: navigator.standalone
Safari на iOS появился раньше этой медиафункции и имеет собственный флаг:
// true: launched from the Home Screen; false: in a Safari tab;
// undefined: not iOS WebKit
const iosStandalone = navigator.standalone === true;
Полезно различие между false и undefined. Браузер, который реализует свойство, но запущен не с домашнего экрана, вернёт false, а Chrome на Windows или Android — undefined. Поскольку все браузеры на iOS построены на WebKit, свойство определяется движком, а не названием приложения.
Практичный детектор объединяет оба варианта:
const installed =
window.matchMedia('(display-mode: standalone)').matches ||
navigator.standalone === true;
Зачем сайтам это проверять
- Аналитика. Разделение сессий на «установлено / вкладка» показывает, пользуются ли установкой и отличается ли поведение таких пользователей.
- Предложение установки. Нет смысла показывать «На экран Домой» тому, кто уже это сделал.
- Различия интерфейса. Без кнопки «назад» и адресной строки standalone-приложению нужна собственная навигация, а странице — дополнительные отступы под безопасную зону.
- Отладка. Жалобы «во вкладке работает, как приложение — ломается» разбирать гораздо проще, если страница записала, в каком контексте работала.
Standalone, WebView и вкладка
Читатели часто путают три контекста, в которых нет привычного интерфейса браузера:
| Контекст | display-mode: standalone | Типичный признак |
|---|---|---|
| Обычная вкладка | Нет (browser) | Полный интерфейс браузера |
| Установленное PWA | Да | Собственное окно, запуск с иконки |
| Встроенный браузер (WebView) | Обычно нет | Маркер wv или особенности user-agent приложения |
Встроенный браузер — это страница, отрисованная внутри другого приложения, например соцсети, и показать её там решило приложение-хозяин, а не пользователь. Установленное PWA пользователь добавил осознанно. Страница, которая проверяет лишь «есть ли адресная строка», перепутает их, поэтому медиазапрос используют вместе с сигналами user-agent.
Ракурс фингерпринтинга
Можно ли по этому сигналу узнать пользователя? Сам по себе почти нельзя: это один бит. Но он дополняет картину двумя способами. Установленный контекст встречается реже вкладки, поэтому слегка сужает круг похожих пользователей — как и другие сигналы медиафункций. И это проверка согласованности: user-agent с iPhone при navigator.standalone равном undefined или «iOS»-профиль на десктопном движке — именно то несоответствие, которое описано в чек-листе согласованности отпечатка. Это ещё один небольшой элемент мобильной поверхности фингерпринтинга.
О самой модели приложений рассказывает руководство MDN по прогрессивным веб-приложениям: манифесты, возможность установки и то, как браузеры показывают установленные приложения.
Проверьте сами
Откройте проверку WebView BrowserInsight в обычной вкладке и запомните результат. Затем добавьте сайт на домашний экран (или установите из меню браузера), откройте с иконки и запустите проверку снова. Сигнал «Автономный режим (PWA)» читает ровно две проверки выше, так что вы увидите, как он переключается. Наша проверка ядра тоже читает navigator.standalone как один из сигналов WebKit.
Часто задаваемые вопросы
Работает ли display-mode на десктопе?
Да. Десктопные браузеры на Chromium могут устанавливать сайт как окно приложения, и оно соответствует (display-mode: standalone). Поддержка и сценарии установки различаются, поэтому используйте проверку возможностей, а не предположения.
Может ли страница узнать, можно ли приложение установить?
Через эти сигналы — нет. Они описывают лишь то, как страница работает сейчас. Предложения установить приложение опираются на отдельные события, зависящие от браузера, а пользователь вкладки, который ничего не устанавливал, выглядит как любой другой.
Нужен ли ещё navigator.standalone?
Современный iOS поддерживает и медиазапрос, так что новый код может опираться на display-mode. Многие сайты всё равно проверяют оба варианта: это всего одна строка, зато покрывает старое поведение.
Можно ли это подделать?
Переключатель user-agent или инструмент автоматизации подделает строку, но согласованная подделка должна ещё и совпадать с реальными свойствами движка — именно это ищут проверки согласованности. Считайте сигнал подсказкой о способе отображения, а не границей безопасности.
Заключение
Определить PWA в standalone-режиме можно двумя строками: медиазапросом display-mode и, для приложений на домашнем экране iOS, navigator.standalone. Страница узнаёт, как её запустили, — это полезно для аналитики, предложений установки и вёрстки и немного информативно для фингерпринтинга, главным образом как проверка согласованности.


