Если установленное PWA не открывает deep link, пользователь нажимает ссылку на товар, заказ или раздел, но попадает на главную страницу, видит обычную вкладку браузера либо получает ошибку 404. Причина не всегда находится в manifest: на результат одновременно влияют адрес ссылки, scope приложения, серверная маршрутизация, service worker, клиентский роутер и правила конкретной операционной системы.
Исправление лучше начинать с воспроизводимого теста. Не меняйте сразу manifest, маршруты и кэш: сначала определите, какой компонент теряет исходный URL. Так можно устранить причину, не сломав запуск PWA для остальных пользователей.
Определите точный сценарий ошибки
- Запишите полный URL, включая путь, query-параметры и fragment. Не заменяйте его описанием вроде «ссылка на товар».
- Уточните источник перехода: мессенджер, письмо, QR-код, браузер, push-уведомление или ссылка внутри PWA.
- Проверьте, установлено ли приложение именно из того браузера, в котором проводится тест.
- Зафиксируйте устройство, версию ОС, браузер и режим отображения приложения.
- Сравните результат при закрытом PWA и при уже запущенном приложении.
- Проверьте тот же URL в обычной вкладке и в приватном режиме.
Важно отличать три неисправности: ссылка не передается установленному приложению, PWA получает ссылку, но заменяет ее стартовым адресом, либо нужный маршрут открывается и затем падает из-за 404, авторизации или старого кэша.
Не путайте запуск PWA и перехват всех ссылок
Установка PWA не гарантирует, что любая ссылка домена всегда будет открываться в отдельном окне приложения. Поведение зависит от браузера, операционной системы, способа установки и пользовательских настроек. На некоторых устройствах ссылка остается в браузере, хотя само PWA настроено правильно.
Поля manifest задают границы и старт приложения, но не являются универсальной заменой Android App Links или iOS Universal Links. Экспериментальные механизмы перехвата ссылок также нельзя считать одинаково поддерживаемыми во всех браузерах. Поэтому сначала добейтесь корректного открытия любого deep link внутри самого PWA, а уже затем проверяйте передачу внешних ссылок приложению.
Проверьте start_url, scope и id в manifest
start_url определяет адрес обычного запуска с иконки. Он не должен принудительно заменять URL, по которому пользователь уже пришел. scope ограничивает область сайта, считающуюся частью приложения. Если deep link находится за ее пределами, браузер вправе открыть обычную вкладку.
- start_url существует, возвращает HTTP 200 и находится внутри scope.
- scope охватывает все маршруты, которые должны открываться в PWA.
- Ссылка использует тот же протокол, домен и ожидаемый путь без лишнего поддомена.
- Manifest отдается с корректным Content-Type и без редиректа на авторизацию.
- Поле id остается стабильным между версиями и не создает вторую идентичность приложения.
- Относительные URL вычисляются относительно адреса manifest, а не исходного HTML-файла.
Типичная ошибка возникает, когда приложение установлено со scope /app/, а внешняя ссылка ведет на /orders/42. Даже при одном домене такой адрес находится вне области PWA. Решением может быть расширение scope либо перенос публичных маршрутов под общий префикс, но изменение должно соответствовать реальной структуре сайта.
Убедитесь, что сервер знает маршрут deep link
Для одностраничного приложения переход через интерфейс и прямое открытие URL работают по-разному. При клике внутри SPA маршрут обрабатывает JavaScript. При внешнем deep link запрос сначала получает сервер. Если Nginx или Apache ищет физический файл и возвращает 404, клиентский роутер даже не запускается.
- Откройте deep link в новой вкладке после очистки кэша.
- Проверьте HTTP-статус и цепочку redirect в инструментах разработчика.
- Для SPA настройте fallback на входной HTML только для пользовательских маршрутов.
- Не направляйте API, статические файлы и служебные endpoints на SPA fallback.
- Для серверного приложения проверьте наличие route и его параметры.
- Убедитесь, что канонизация slash и регистра не создает цикл редиректов.
Рабочий тест прост: нужный URL должен корректно открываться после полной остановки браузера, без предварительного посещения главной страницы. Если это не так, проблема находится ниже уровня PWA.
Проверьте обработку navigation в service worker
Service worker может перехватывать навигационный запрос и отдавать закэшированный index.html. Это полезно для offline-режима, но опасно, если обработчик игнорирует исходный URL, маскирует серверные ошибки или возвращает старую оболочку, несовместимую с текущими маршрутами.
- Обрабатывайте navigation отдельно от запросов API и ресурсов.
- Не заменяйте request URL значением start_url на каждом переходе.
- Сохраняйте исходный pathname, query и fragment для клиентского роутера.
- Используйте понятную стратегию обновления app shell и версионирование кэшей.
- Не кэшируйте персональные HTML-ответы общего доступа.
- Предусмотрите offline-страницу, если маршрут нельзя восстановить без сети.
Для диагностики временно отключите service worker в инструментах разработчика и повторите переход. Если deep link начинает работать, проверяйте fetch-handler и содержимое Cache Storage. Если ничего не меняется, ищите ошибку в manifest, сервере или роутере.
Проверьте клиентский роутер
После загрузки app shell роутер должен прочитать текущий location, а не всегда выполнять переход на главную. Принудительный redirect часто спрятан в инициализации приложения, восстановлении сессии или обработчике первого запуска.
- Начальный route строится из текущего URL.
- Middleware авторизации сохраняет адрес возврата.
- Неизвестный маршрут показывает корректную страницу 404, а не молча открывает главную.
- Query-параметры не теряются при нормализации URL.
- Переход после загрузки профиля выполняется только при необходимости.
- History API и base path совпадают с каталогом размещения приложения.
Добавьте временный лог начального location и всех redirect. Он быстро показывает момент, когда /orders/42 превращается в /. После исправления диагностический лог следует убрать или обезличить.
Не теряйте deep link при авторизации
Закрытый маршрут может законно отправить пользователя на вход. Ошибка появляется, когда после авторизации приложение забывает исходный адрес и всегда открывает кабинет или главную. Адрес возврата нужно хранить безопасно и проверять перед использованием.
- Сохраняйте только внутренний относительный путь, а не произвольный внешний URL.
- Проверяйте допустимый origin, чтобы не создать open redirect.
- Не помещайте секретные данные и токены в query-параметры.
- Учитывайте SameSite, Secure, Path и Domain у cookie.
- После успешного входа удаляйте одноразовый адрес возврата.
- Проверяйте сценарий истекшей сессии при запуске установленного PWA.
Сравните поведение Android и iOS
Один успешный тест на desktop не подтверждает работу deep links на телефонах. Android и iOS по-разному связывают установленные веб-приложения с внешними ссылками. Кроме того, ссылки из встроенного браузера мессенджера могут не передаваться системному браузеру так же, как ссылки из почты.
- Проверьте установку и запуск в поддерживаемом браузере каждой платформы.
- Повторите тест из обычного браузера, почты и одного популярного мессенджера.
- Учитывайте пользовательский выбор приложения для открытия ссылок.
- Не обещайте автоматический перехват внешних ссылок там, где платформа его не гарантирует.
- Для нативной оболочки отдельно настройте и проверьте App Links или Universal Links.
- Документируйте допустимый fallback: открыть корректную страницу в браузере лучше, чем потерять маршрут.
Проверьте параметры, кодировку и короткие ссылки
Deep link может работать для простого пути и ломаться только при наличии UTM-меток, кириллицы, encoded slash или fragment. Сервисы сокращения ссылок и рекламные трекеры добавляют redirect, который также влияет на выбор окна и передачу параметров.
- Сравните исходный и конечный URL после всех redirect.
- Не декодируйте pathname повторно на клиенте.
- Проверяйте обязательные параметры до обращения к API.
- Сохраняйте fragment, если роутер использует hash-навигацию.
- Не меняйте protocol или поддомен в середине цепочки.
- Проверьте ссылки с пробелами, кириллицей и зарезервированными символами.
Безопасный порядок исправления
- Воспроизведите ошибку на конкретном устройстве и сохраните полный URL.
- Проверьте прямое открытие адреса в обычной вкладке и HTTP-ответ сервера.
- Сверьте origin, scope, start_url и id установленного manifest.
- Отключите service worker для контрольного теста.
- Проверьте начальный route и цепочку redirect в приложении.
- Исправьте один подтвержденный уровень, не меняя остальные одновременно.
- Выпустите новую версию app shell и service worker с контролируемым обновлением.
- Переустановите PWA только как дополнительный тест, а не как единственный способ лечения.
- Повторите переход из браузера, мессенджера, письма и push-уведомления.
- Добавьте автоматические проверки прямых маршрутов в процесс публикации.
Как проверить результат
- Deep link открывает требуемый экран, а не start_url.
- Path, query и fragment сохраняются без искажений.
- Неавторизованный пользователь возвращается к исходному экрану после входа.
- Прямой запрос маршрута получает ожидаемый HTTP-статус.
- Старая и новая версии service worker не создают цикл обновления.
- При неподдерживаемом перехвате ссылка корректно открывается в браузере.
- Поведение подтверждено минимум на Android и iOS, если обе платформы входят в требования.
Типичные ошибки
- Считать, что start_url должен содержать каждый возможный маршрут.
- Расширять scope на весь домен без проверки безопасности и структуры сайта.
- Лечить серверный 404 переустановкой приложения.
- Всегда перенаправлять пользователя на главную после проверки сессии.
- Кэшировать один HTML для всех navigation без учета обновлений.
- Проверять только ссылку внутри уже открытого PWA.
- Смешивать поведение обычного PWA с возможностями нативного приложения.
- Использовать внешний return URL без защиты от open redirect.
Как предотвратить повторение
- Храните набор контрольных deep links для основных пользовательских сценариев.
- Проверяйте manifest и service worker в CI перед публикацией.
- Запускайте smoke-тест прямого открытия маршрутов на production-like окружении.
- Версионируйте кэши и контролируйте активацию нового service worker.
- Документируйте поддерживаемое поведение внешних ссылок по платформам.
- Отслеживайте 404, циклы redirect и ошибки первого запуска отдельно.
- Не связывайте восстановление сессии с безусловным переходом на главную.
Когда нужна помощь
Если установленное PWA теряет deep link, открывает главную или уходит в обычную вкладку, можно прислать адрес сайта, пример ссылки, модель устройства и описание ожидаемого экрана. Я проверю manifest, серверные маршруты, service worker, авторизацию и клиентскую навигацию, найду место потери URL и предложу безопасное исправление без лишней переделки приложения.