Диплинк (deep link) - это просто URL, который операционная система передает приложению вместо браузера. Нажмите на ссылку на устройстве, где установлено приложение, - и оно откроется на нужном экране. Нажмите на нее там, где приложения нет, - и браузер перейдет по запасному пути (fallback) на веб-страницу. Один URL, два исхода, никаких JavaScript-прослоек и сторонних SDK для большинства случаев.
Команды часто переплачивают за это. Маркетинговые материалы Branch.io, Adjust, AppsFlyer делают упор на отложенный диплинкинг (deferred deep linking): вы нажимаете на ссылку до установки приложения, устанавливаете его, и оно открывается именно на том контенте, по которому вы перешли. Эта функция действительно сложна и требует серверного фингерпринтинга или трюков с буфером обмена, так как маршрутизация ссылок в ОС не активна до установки приложения. Но отложенный диплинкинг - это лишь часть проблемы. Более распространенный случай - «ссылка открывается в уже установленном приложении» - полностью решается нативными примитивами ОС, которые Apple и Google выпустили в 2015 и 2015–16 годах соответственно. Для этого не нужно ничего, кроме вашего домена и двух JSON-файлов.
Этот пост посвящен именно этим двум JSON-файлам.
Кратко#
- Apple Universal Links (iOS 9+) и Android App Links (Android 6.0+) обеспечивают сценарий «открыть в приложении, если установлено, иначе в браузере» без сторонних SDK.
- Оба метода требуют контроля над доменом, работы через HTTPS и наличия проверенного файла ассоциации в директории
/.well-known/. ОС загружает и кэширует этот файл при установке приложения, а не при каждом нажатии. - Сокращатель ссылок с кастомным доменом, например
go.acme.example, отдает оба файла и становится точкой входа для маршрутизации - короткая ссылка сама по себе является диплинком. - Что добавляет SDK по сравнению с нативными средствами ОС: отложенный диплинкинг, вероятностную атрибуцию установок и связывание личностей между платформами. Если вам это нужно, SDK оправдывает свою стоимость. Если нет - вы платите за функции, которыми не пользуетесь.
Нативные механизмы ОС#
Apple представила Universal Links в iOS 9 (2015). Android выпустил App Links в Android 6.0 Marshmallow (также в 2015 году). Оба решения следуют одной концептуальной модели: ОС подтверждает проверенную связь между доменом и приложением, и когда происходит переход по URL на этом домене, ОС направляет его в приложение, а не в браузер.
Проверка является взаимной и работает по принципу offline-first. При установке приложения ОС загружает файл ассоциации с вашего домена и кэширует его. Механизм загрузки Apple документирован на developer.apple.com/ios/universal-links/ (доступ от 12.05.2026); эквивалент от Google находится на developer.android.com/training/app-links (доступ от 12.05.2026). Ни одна из этих загрузок не происходит в момент нажатия на ссылку на уже настроенном устройстве - кэширование означает, что решение о маршрутизации не требует сетевых запросов.
Сам редирект - это стандартный HTTP 302. ОС перехватывает его до того, как загрузится браузер, проверяет локальный кэш и передает URL приложению, если найдено соответствие. Как только кэш прогрет, всё решение принимается локально. Край (edge), обслуживающий короткую ссылку, выдает редирект, и ОС берет управление на себя.
Два файла#
apple-app-site-association#
Файл AASA должен обслуживаться по адресу https://yourdomain.example/.well-known/apple-app-site-association (Apple также проверяет путь в корне https://yourdomain.example/apple-app-site-association для обратной совместимости, но стандартным сейчас является путь .well-known). Он должен отдаваться через HTTPS с валидной цепочкой сертификатов и заголовком Content-Type: application/json. Загрузчик CDN Apple отклоняет файлы с неверным Content-Type - это одна из самых частых ошибок конфигурации.
Полный справочник формата доступен на developer.apple.com/documentation/xcode/supporting-associated-domains.
Минимальная структура AASA:
{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.acme"],
"components": [
{
"/": "/spring-*",
"comment": "Match any path starting with /spring-"
},
{
"/": "/campaigns/*"
}
]
}
]
}
}
appIDs - это конкатенация вашего Apple Team ID и bundle identifier вашего приложения, разделенные точкой. Массив components управляет тем, какие пути запускают маршрутизацию в приложение; всё, что не соответствует компоненту, открывается в браузере. Вы можете зарегистрировать несколько приложений в массиве details - это полезно, если у вас есть потребительская и корпоративная версии одного и того же продукта на одном домене.
Один важный нюанс: "/" с шаблоном вроде /spring-* - это сопоставление префикса пути. Парсер AASA от Apple поддерживает синтаксис шаблонов, определенный в документации Xcode, включая * (любая подстрока), ? (любой одиночный символ) и объекты исключения. Если вы хотите сопоставить все пути на домене, используйте "/" : "/*". Если вы хотите исключить конкретный путь из маршрутизации - скажем, страница /account/delete всегда должна открываться в браузере - добавьте объект исключения перед универсальным шаблоном:
{
"/": "/account/delete",
"exclude": true
}
Правила вычисляются сверху вниз. Ставьте исключения перед шаблонами с подстановочными знаками.
assetlinks.json#
Файл Digital Asset Links для Android живет по адресу https://yourdomain.example/.well-known/assetlinks.json. Спецификация поддерживается Google на developers.google.com/digital-asset-links/v1/getting-started.
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.acme",
"sha256_cert_fingerprints": [
"AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"
]
}
}
]
package_name - это application ID вашего приложения в Play Store. sha256_cert_fingerprints - это SHA-256 отпечаток сертификата, использованного для подписи APK (именно SHA-256, не SHA-1 и не MD5). Вы можете найти отпечаток в Play Console в разделе App Integrity или запустив команду keytool -list -v -keystore your.keystore. Если вы выпускаете и debug, и production сборки, включите оба отпечатка в массив.
В отличие от файла AASA, файл ассоциации Android не поддерживает фильтрацию путей на уровне файла. Сопоставление путей для App Links настраивается в AndroidManifest.xml через <intent-filter> с использованием android:pathPrefix, android:pathPattern или более нового android:pathAdvancedPattern (доступно с Android 12). Файл assetlinks.json подтверждает владение доменом; манифест декларирует, какие пути обрабатывает приложение.
Как вписывается сокращатель ссылок#
Короткая ссылка вроде go.acme.example/spring-launch - это просто URL на домене. С точки зрения ОС, если у go.acme.example есть валидный файл AASA или assetlinks.json, любое нажатие на ссылку под этим доменом может привести к открытию приложения.
Эту конфигурацию мы поддерживаем напрямую для кастомных доменов в Elido. Когда вы регистрируете go.acme.example как кастомный домен в своем рабочем пространстве, Elido обслуживает HTTPS-редирект для каждого слага (slug) на этом домене. Вы отдаете два файла ассоциации с того же домена - либо со своего собственного бэкенда за прокси-путем, либо через собственный HTTPS-сервер домена. Срабатывает редирект на крае; ОС перехватывает его до загрузки браузера, обращается к кэшу AASA/App Links и открывает приложение, если найдено соответствие.
Архитектура более подробно описана в посте о кастомных доменах для коротких ссылок - принципы выпуска TLS и настройки CNAME применяются здесь точно так же. Слой диплинков является аддитивным: тот же домен, тот же редирект, плюс два JSON-файла.
Для продуктовых команд, использующих короткие ссылки для онбординга в мобильных приложениях - реферальные коды, инвайт-ссылки, функции «поделиться рецептом» - этот паттерн покрывает почти всё без добавления зависимостей SDK в бинарный файл приложения.
Что дает использование SDK#
Три возможности, которые не предоставляют нативные примитивы ОС:
Отложенный диплинкинг. Пользователь нажимает на вашу ссылку до установки приложения. При первом запуске после установки приложение открывается именно на том контенте, по которому он перешел. iOS Universal Links и Android App Links бездействуют, если приложение не установлено - URL открывается в браузере, и намерение теряется. Его восстановление требует серверного сопоставления отпечатков (IP + User-Agent + временная метка, вероятностный метод) или трюка с буфером обмена в iOS. Branch, Adjust и AppsFlyer реализуют это; нюансы вокруг запросов App Tracking Transparency и поведения Safari делают самостоятельную реализацию нетривиальной.
Атрибуция установок в масштабе. Нативный путь ОС дает вам факт открытия приложения с URL-путем, но если приложение не было установлено в момент первого нажатия, цепочка атрибуции разрывается. Сопоставление кликов с установками через SKAN на iOS и Play Install Referrer на Android возможно без платных SDK, но требует интеграционной работы, которую вендоры атрибуции уже проделали.
Кросс-платформенное связывание личностей. Привязка нажатия к адресу электронной почты, контакту в CRM или веб-сессии. Нативный путь ОС анонимен с точки зрения сервиса ссылок. Вендоры SDK поддерживают постоянный граф устройств. Создание такого графа своими силами - это масштабный проект по созданию инфраструктуры данных.
Если ни один из этих трех пунктов не является критичным, нативных средств ОС вам будет достаточно. Если что-то из этого важно, четко определите область применения - возможно, вам нужен только API для отложенных диплинков, а не весь SDK целиком.
Рецепты настройки#
Требования к DNS и HTTPS#
Оба файла должны отдаваться через HTTPS с того домена, ссылки с которого вы хотите сделать глубокими. Сертификат должен иметь цепочку к доверенному корневому удостоверяющему центру; самоподписанные сертификаты приведут к тихой ошибке валидаторов Apple и Google. Сертификаты Let's Encrypt подходят отлично.
TLS домена также не должен перенаправлять путь /.well-known/ до выдачи файла. Если ваш сервер выдает редирект на www. до того, как загрузчик Apple сможет добраться до https://yourdomain.example/.well-known/apple-app-site-association, загрузка не удастся. Загрузчик Apple следует максимум по одному редиректу, но загрузчик assetlinks от Google вообще не следует по редиректам - файл должен быть по точному адресу.
iOS: Настройка Associated Domains#
В Xcode, в разделе Signing & Capabilities вашей цели (target), добавьте Associated Domains со значением applinks:go.acme.example. Если вы тестируете на разрабатываемой сборке (не распространяемой через TestFlight или App Store), добавьте ?mode=developer к значению: applinks:go.acme.example?mode=developer. Это заставляет ОС заново загружать AASA при каждом запуске вместо использования кэша времени установки - удобно для отладки шаблонов путей без переустановки приложения.
CDN Apple, который загружает ваш файл AASA, - это собственная инфраструктура Apple, а не само устройство. Apple предварительно загружает и кэширует AASA-файлы с вашего домена и раздает их устройствам во время установки приложения. Это означает, что файл должен быть доступен для краулеров Apple, а не только для устройства конечного пользователя. Также это подразумевает задержку распространения - изменения в файле AASA могут достигать всех устройств через кэш Apple в течение нескольких часов. Для разработчиков, которым нужна быстрая итерация по шаблонам путей, параметр ?mode=developer позволяет обходить CDN Apple и загружать файл напрямую с вашего сервера.
Android: App Links в манифесте#
В AndroidManifest.xml внутри activity, которая должна обрабатывать диплинки:
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="go.acme.example"
android:pathPrefix="/campaigns/" />
</intent-filter>
android:autoVerify="true" сообщает Android о необходимости попытки верификации домена через assetlinks.json. Без этого пользователь будет видеть окно выбора приложения при каждом нажатии вместо прямого открытия в приложении. Верификация происходит при установке; устройство обращается к https://go.acme.example/.well-known/assetlinks.json и проверяет наличие отпечатка сертификата установленного приложения.
Валидаторы и типичные ошибки#
Apple предоставляет валидатор на search.developer.apple.com/appsearch-validation-tool/, который проверяет, может ли CDN Apple загрузить и распарсить ваш файл AASA. Введите свой домен, и он вернет либо результат парсинга, либо конкретную ошибку. Типичные сбои:
- Неверный Content-Type. Если ваш сервер возвращает
text/plainилиapplication/octet-stream, валидатор сообщит, что файл не читается, даже если JSON корректен. Явно установитеContent-Type: application/json. - Отсутствующий или неверный
appIDs. Префикс Team ID вappIDsдолжен точно соответствовать Team ID в вашем аккаунте Apple Developer, включая регистр. Ошибка в одном символе приведет к тихой неудаче при нажатии. - Проблемы с цепочкой сертификатов. Если домен использует сертификат, который не ведет к публичному корню (часто бывает в стейджинг-средах с локальными корнями CA), загрузчик Apple отклонит файл.
Валидатор Google - это Digital Asset Links API: https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://go.acme.example&relation=delegate_permission/common.handle_all_urls. JSON-ответ содержит список всех утверждений, которые Google подтвердил для вашего домена. Если ответ пуст или в нем нет имени вашего пакета, Android не подтвердит приложение автоматически при установке. Типичные сбои:
- Редирект на пути к assetlinks. Как уже отмечалось: загрузчик Google не следует по редиректам.
- Неверный отпечаток сертификата. Debug APK и release APK подписаны разными ключами. Если вы указали только отпечаток для релиза, отладочные сборки не пройдут проверку. Укажите оба.
- Файл отдается с CORS, но неверным заголовком для проверочного запроса. Загрузчику не важен CORS, но некоторые конфигурации CDN возвращают 403 при
GETзапросе из диапазонов IP Google, если путь не в белом списке кэширования. Проверьте, что/.well-known/assetlinks.jsonвозвращает 200 через внешний HTTP-клиент, а не только в вашем браузере.
Честный взгляд на компромиссы#
Отказ от SDK для диплинков экономит примерно 150–250 КБ сжатого размера бинарного файла и исключает один API-вызов к серверам вендора атрибуции при каждой установке. Это убирает пункт о передаче данных из вашей политики конфиденциальности и может упростить отчетность по GDPR. Это реальные, хотя и скромные победы.
Ценой является то, что атрибуция на момент установки становится приблизительной или отсутствует вовсе. Если пользователь нажмет на ссылку до установки приложения, вы увидите установку в App Store Connect или Play Console, но не сможете связать ее с конкретным нажатием. Вы всё еще сможете проводить эксперименты со смарт-ссылками, чтобы сравнивать, какие кампании приносят больше установок - относительные сигналы сохраняются, - но атрибуция на уровне конкретного устройства и клика требует слоя фингерпринтинга, который есть в SDK.
Для команд на ранних этапах масштабирования, где CPI по каналам еще не определяет бюджетные решения, использование нативных средств ОС - разумный путь. Добавить SDK для атрибуции можно позже, когда эти данные станут действительно необходимы для действий.
Для разработчиков в руководстве по настройке диплинков в Elido описаны полная схема процесса верификации домена, конфигурация прокси AASA и варианты обслуживания assetlinks.json. Страница решений для продуктовых команд охватывает более широкие сценарии использования смарт-ссылок и мобильного взаимодействия.
Мариус Фосс - DevRel + edge infra в Elido. Он отвечает за сервисы edge-redirect и domain-manager.
Попробуйте Elido
Вставьте URL - получите короткую ссылку
Без регистрации. Ссылка живёт 30 дней. Зарегистрируйтесь, чтобы оставить её навсегда.
Бесплатно, без регистрации · 2 в день