Universal Links and Android App Links Explained: How Mobile Deep Linking Protocols Work (2026)
Learn how Apple Universal Links and Android App Links route mobile users directly into app content, how AASA and assetlinks.json domain verification work, and how branded links handle fallback redirects.

Key takeaways
- Dual-sided domain verification eliminates URL hijacking: Apple Universal Links and Android App Links replace unverified URI schemes with standard HTTPS URLs validated by cryptographic handshakes: Apple's
apple-app-site-association(AASA) file on iOS and Google's Digital Asset Links (assetlinks.json) on Android. - Direct user routing without browser dialogs: Verified HTTPS deep links bypass "Open in App?" browser confirmation prompts and Android intent picker dialogs, routing installed users straight to in-app views.
- Fail-safe fallback mechanics: When an app is not installed, standard HTTPS links do not produce broken browser errors. Instead, they fall back to web pages, custom landing pages, or app store product pages.
- In-app webview sandboxes require active handling: Embedded browsers inside social applications (such as Meta, TikTok, and other platforms) often intercept clicks within sandboxed webviews, breaking direct operating system routing unless handled via specialized JavaScript or trampoline routing.
- Custom branded domains preserve link trust and tracking: Using branded subdomains (e.g.,
links.yourbrand.com) configured with CNAME records preserves marketing attribution parameters without breaking OS-level domain entitlement checks.
Protocol architecture: Custom URI schemes vs. standard HTTPS deep linking
Early mobile deep linking relied entirely on custom URI schemes (such as myapp://path/to/content). As documented by Android Developers, custom URI deep linking takes advantage of the intent system to route links to an app, but these links remain subject to the system disambiguation dialog and lack ownership verification. While straightforward to register in an app manifest, custom URI schemes introduce fundamental security and user experience limitations.
The problems with custom URI schemes
- No domain ownership verification (Collision & Hijacking): Any application can register the same custom scheme (e.g.,
fitnessapp://). If a user has two apps installed that claim the same scheme, the operating system cannot reliably determine which app should handle the request. Malicious apps can register competitor schemes to intercept incoming link payloads and sensitive tokens. - Disambiguation prompts: Because the OS cannot confirm domain ownership, users often encounter modal confirmation dialogs ("Open with...") before navigating to the app.
- Broken fallback handling: If the requested app is not installed, clicking a custom URI scheme inside a standard web browser leads to a hard browser error (such as "Address unreachable" or "Cannot open page").
The HTTPS standard: Universal Links and App Links
To solve these vulnerabilities, Apple introduced Universal Links in iOS 9, and Google introduced Android App Links in Android 6.0 (API level 23) as detailed in Android's App Links verification documentation. Both protocols bind standard web URLs (https://) directly to an application through bidirectional domain verification.
According to Apple Developer Documentation, Universal Links allow an app to present native content in place of all or part of its website, while users who do not have the app installed receive the same content in a web browser.
If the app is installed, the mobile operating system intercepts the HTTPS request and routes the user directly into native code without opening the web browser disambiguation dialog. If the app is not installed, the request falls back cleanly to the web server, which can serve the web page or redirect the user to the Apple App Store or Google Play Store.
Apple Universal Links: Architecture, AASA verification, and iOS routing
According to Apple Developer Documentation, associated domains provide the underpinning to Apple Universal Links, allowing iOS apps to claim ownership over standard web domains. When an iOS device opens a verified Universal Link, iOS routes the user directly into the app using native API entry points.
The apple-app-site-association (AASA) file
The foundation of Universal Links is the apple-app-site-association file. As outlined by Apple, this JSON file is hosted on your web domain to define which apps and app IDs are authorized to handle specific URL paths.
{
"applinks": {
"apps": [],
"details": [
{
"appIDs": [
"TEAMID1234.com.example.app"
],
"components": [
{
"/": "/workout/*"
},
{
"/": "/subscribe"
},
{
"/": "/checkout/*",
"exclude": true
}
]
}
]
}
}
AASA hosting requirements
- Path: As specified by Apple Developer Documentation, the file must be accessible at either
https://<domain>/.well-known/apple-app-site-associationorhttps://<domain>/apple-app-site-association. - HTTPS & Certificates: Must be served over a valid HTTPS connection with a trusted TLS certificate (self-signed certificates are rejected).
- MIME Type: Must be served with
Content-Type: application/json. - Redirects: The server must not return any HTTP redirects (301 or 302). iOS expects an immediate
200 OKresponse. - Apple CDN Caching: Starting with iOS 14 and macOS 11, Apple notes that apps send requests for AASA files to an Apple-managed content delivery network (CDN) rather than querying your web server directly.
Xcode Associated Domains configuration
To enable Universal Link handling in the client application, Apple Developer Documentation specifies setting up the Associated Domains entitlement:
- In Xcode, navigate to your app target's Signing & Capabilities.
- Add the Associated Domains capability.
- Add the domain with the
applinks:prefix:applinks:links.example.com - If you support Universal Links across development and production environments, you can append
?mode=developerfor local debugging builds.
Handling incoming Universal Links in iOS
When a user clicks a Universal Link, UIKit calls the application delegate method:
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let incomingURL = userActivity.webpageURL else {
return false
}
// Parse path components and query parameters
let path = incomingURL.path
let queryParams = incomingURL.query
// Route to the appropriate in-app view controller
Router.shared.navigate(to: path, params: queryParams)
return true
}
In SwiftUI apps using iOS 14 and later, incoming URLs can be handled directly using the .onOpenURL modifier:
@main
struct SubscriptionApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
Router.shared.handle(url: url)
}
}
}
}
Android App Links: Digital Asset Links, SHA-256 verification, and intent filters
As documented by Android Developers, Android App Links are verified HTTP/HTTPS deep links that establish a trusted association between an app and website, immediately opening corresponding in-app content without triggering the system disambiguation dialog ("Open with...").
Digital Asset Links (assetlinks.json)
As detailed in Android's App Links verification guide, the Android system queries the Digital Asset Links file hosted at https://<domain>/.well-known/assetlinks.json for each unique hostname declared in the intent filters.
[
{
"relation": [
"delegate_permission/common.handle_all_urls"
],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": [
"14:6D:E9:7D:0F:52:CC:45:25:4E:69:E2:E0:64:4F:E8:EE:00:2E:81:97:04:EB:37:4E:6B:43:31:8B:D8:0C:71"
]
}
}
]
Extracting your SHA-256 fingerprint
To get the SHA-256 fingerprint from your release keystore using the Java keytool command:
keytool -list -v -keystore my-release-key.keystore -alias my-key-alias
If you use Google Play App Signing, you must retrieve the SHA-256 fingerprint from the Google Play Console under Release > Setup > App Integrity > App Signing Certificate, because Google re-signs your production APK before distribution.
Configuring AndroidManifest.xml
As explained in Android Developers documentation, when android:autoVerify="true" is declared in an intent filter, installing the app on Android 6.0 (API level 23) or higher causes the OS to automatically verify the declared hosts:
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask">
<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" />
<data android:host="links.example.com" />
<data android:pathPrefix="/workout" />
<data android:pathPrefix="/subscribe" />
</intent-filter>
</activity>
android:autoVerify="true": Instructs Android OS to fetchassetlinks.jsonfrom the specified host during installation. If verification succeeds, the OS automatically handles links from this host without prompting the user.
Handling incoming App Links in Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
handleIntent(intent)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent)
intent?.let { handleIntent(it) }
}
private fun handleIntent(intent: Intent) {
val action: String? = intent.action
val data: Uri? = intent.data
if (Intent.ACTION_VIEW == action && data != null) {
val path = data.path
val campaignSource = data.getQueryParameter("utm_source")
// Pass route to internal navigation engine
AppNavigator.navigate(path, campaignSource)
}
}
Branded domain masking, in-app webview sandboxes, and fallback routing
In production growth marketing, links rarely point directly to raw app endpoints. Instead, teams use branded routing links (e.g., https://go.yourbrand.com/summer-sale) to track marketing campaigns across channels while routing users directly into the app.
Branded domain CNAME configuration
A branded deep link uses your custom domain while routing DNS traffic to your deep linking and measurement infrastructure.
- DNS Setup: Create a CNAME record with your DNS provider (e.g., Cloudflare, Route 53):
CNAME links.yourbrand.com --> custom.airbridge.io - Automated Verification Hosting: The endpoint server automatically serves both
/.well-known/apple-app-site-associationand/.well-known/assetlinks.jsonfor that custom subdomain, matching your app credentials. - Parameter Preservation: Standard attribution parameters, such as UTM parameters detailed by Ortto, AgencyAnalytics, and Ateko, are parsed and retained throughout redirects and in-app payloads.
The in-app webview sandboxing challenge
One of the most frequent failure points in mobile deep linking occurs within social platforms (such as Meta applications like Instagram and Facebook, TikTok, or other feed-based platforms). When a user taps a link inside these apps, the host application does not pass the click to the system browser (Safari or Chrome). Instead, it loads the link inside a sandboxed in-app webview (WKWebView on iOS or embedded WebView on Android).
| Operating Environment | iOS Universal Link Behavior | Android App Link Behavior |
|---|---|---|
| Native Safari / Chrome | Direct native app open (Zero prompts) | Direct native app open (Zero prompts) |
| Mail / Notes / Messages | Direct native app open | Direct native app open |
| In-App WebViews (Meta, TikTok) | Sandboxed in webview; direct OS routing intercepted | Sandboxed in webview; direct OS routing intercepted |
| Typing directly into URL bar | Safari loads webpage (Universal Link bypassed) | Chrome loads webpage (App Link bypassed) |
How to bypass in-app webview sandboxing
To ensure users reach the native app from sandboxed webviews, routing engines use specialized fallback mechanics:
- Trampoline landing pages: As suggested in Android documentation, introducing a trampoline activity or landing page allows the user to open the link in the first-party app. If a webview user-agent is detected, the server serves a lightweight intermediate page that executes a client-side JavaScript redirect or prompts an explicit user touch event.
- Android Intent URIs: On Android, serving an intent-based scheme (
intent://path#Intent;scheme=https;package=com.example.app;end) forces the in-app webview to yield execution to the Android OS package manager. - Universal Link touch triggers: On iOS, Universal Links require a distinct user interaction (such as tapping a button on a web page) if triggered from within certain iframe or script-based redirects.
Technical comparison: Protocols and capabilities
Understanding the technical boundaries between custom URI schemes, Universal Links, and Android App Links helps growth teams design resilient routing rules.
| Protocol / Capability | Custom URI Schemes (scheme://) | Apple Universal Links (https://) | Android App Links (https://) |
|---|---|---|---|
| Underlying Protocol | Custom URI scheme | Standard HTTP/HTTPS | Standard HTTP/HTTPS |
| Verification File | None | apple-app-site-association | assetlinks.json |
| Verification Location | Client manifest only | /.well-known/apple-app-site-association | /.well-known/assetlinks.json |
| OS Verification Timing | Runtime | App install / update (via Apple CDN) | App install / update (via Google verification) |
| Hijacking Protection | Low (Any app can register) | High (Cryptographic domain verification) | High (SHA-256 fingerprint matching) |
| Browser Prompts | Modal confirmation prompt | None (Direct native routing) | None (Direct native routing) |
| App Absent Fallback | Hard browser error | Web URL or App Store redirect | Web URL or Google Play redirect |
| Webview Interception | Frequently blocked | Sandboxed without trampoline routing | Sandboxed without trampoline routing |
Deep linking workflow and marketing attribution
When an existing subscriber clicks a promotional deep link, the link routes them directly to a target in-app view (such as an upgraded tier or seasonal challenge). If an uninstalled user clicks the link, attribution engines capture the click parameters, route the user to the app store, and attribute the subsequent install upon first launch. Post-install revenue and subscription events can then be tied back to the initial campaign, aligning with measurement models described in marketing analytics literature from Adjust and AppsFlyer.
For engineering and growth teams managing cross-platform campaigns across mobile, web, PC, console, and CTV, Airbridge provides dedicated smart links and deep linking management. Teams focusing purely on routing can use the Airbridge DeepLink Plan (free for the first 10,000 MAU, then $3 per 1,000 MAU). For full-funnel attribution, raw data export, and automated fraud detection without annual lock-in, teams can use the self-serve Airbridge Core Plan ($40+/mo with a 30-day free trial, including 500,000 data points per month and $0.0001 per additional data point).
Why is my Universal Link opening in Safari instead of the app?
When a Universal Link opens in Safari instead of your app, check the following common failure points:
- Apple CDN cache delay: Test against your domain directly by configuring developer mode (
applinks:example.com?mode=developer) in your Xcode entitlements. - AASA JSON formatting: Verify that
apple-app-site-associationcontains valid JSON without trailing commas, returns a200 OKstatus without redirects, and is served withContent-Type: application/json. - App ID formatting: Ensure the
appIDsarray contains your exactTeamID.BundleIdentifierprefix. - User-disabled routing: If a user taps the domain URL banner in Safari's top navigation bar, iOS disables Universal Link routing for that domain on that device. To re-enable it, long-press the link in Notes or Messages and select Open in [App Name].
How do I verify Android App Links using adb?
You can force Android OS to execute App Link verification using Android Debug Bridge (adb) without reinstalling the app:
# Check current verification status
adb shell pm get-app-links com.example.app
# Reset verification state
adb shell pm reset-app-links com.example.app
# Force verification process
adb shell pm verify-app-links --re-verify com.example.app
Check the verification status output. According to Android Developers documentation, if the domain state returns verified, your assetlinks.json and manifest filters are properly configured; any other state indicates verification could not be performed.
Why do Universal Links fail when clicking a link on the same domain?
Apple Universal Links are designed not to hijack internal navigation while a user is already browsing your website. If a user is on https://example.com/page-a and clicks a link pointing to https://example.com/page-b, iOS assumes the user intends to stay within the Safari browser experience. Universal Links only trigger when navigated from an external context (e.g., Mail, Notes, Messages, or a different domain/subdomain). Using a dedicated subdomain (such as links.example.com) ensures links originating from example.com trigger the app.
Can one domain support both Apple Universal Links and Android App Links?
Yes. A single HTTPS domain (e.g., links.yourbrand.com) can host both /.well-known/apple-app-site-association and /.well-known/assetlinks.json simultaneously. As specified by Apple and Android, when an iOS device interacts with the link, it parses the AASA file; when an Android device interacts with the link, it parses assetlinks.json. Desktop web browsers that open the URL will ignore both verification files and load the default web page or configured fallback destination.
Get Started Free
See how these criteria hold up on the real thing.


