What 'Deep Linking' Actually Means
Deep linking is the mechanism that lets a link — in a text message, an email, a push notification, a QR code, a social post — open your app directly to a specific piece of content, instead of dropping the user on a generic home screen or, worse, a browser tab. Most teams say "we need deep linking" as if it's one feature; it's actually four related but distinct mechanisms, and conflating them is where a lot of production bugs start.
| Mechanism | What It Looks Like | How It's Verified | Strength | Weakness |
|---|---|---|---|---|
| Custom URL scheme | myapp://path?param=value | None built-in — first-come, first-registered at the OS level | No server dependency, works offline | Any app can register the same scheme; not a safe target for auth callbacks without added checks |
| iOS Universal Link | https://yourapp.com/path (standard HTTPS URL) | Signed apple-app-site-association file + Associated Domains entitlement, matched on Team ID + Bundle ID | Falls back gracefully to your website if the app isn't installed; cryptographically tied to your verified domain | Verification is cached client-side; a hosting misconfiguration (redirect, wrong content type) can silently break it |
| Android App Link | https://yourapp.com/path (standard HTTPS URL) | assetlinks.json (Digital Asset Links) + autoVerify="true" intent filter | Same graceful web fallback; verified per-domain via Google's Digital Asset Links protocol | Verification failures fall back to a disambiguation dialog on older Android; Android 15+ re-verifies periodically but changes can take up to 7 days to propagate |
| Deferred deep link | Any of the above, resolved after a fresh install | No native OS mechanism — requires a vendor SDK or custom server-side matching logic | Routes a brand-new user straight to relevant content instead of a generic home screen | Post-ATT, relies on probabilistic matching with real accuracy limits — see the deferred deep linking section below |
If you're still deciding on a cross-platform framework before any of this becomes relevant, our custom mobile app development guide and cross-platform development guide cover the platform-native versus cross-platform tradeoff more broadly — React Native and Flutter both have workable Universal Links/App Links wrappers, but as with in-app purchases, you'll still end up reading each platform's own documentation for anything past the basic case.
iOS Universal Links: The Association File and Its Failure Modes
A Universal Link is a normal HTTPS URL — https://yourapp.com/products/42, not a special prefix — that iOS routes to your app instead of Safari when it recognizes the domain as verified for your app. That verification runs through two pieces that both have to agree: an apple-app-site-association (AASA) file your server hosts, and an Associated Domains entitlement (applinks:yourapp.com) your app declares in Xcode.
The AASA file is a JSON document — despite the filename carrying no .json extension — listing which URL paths on your domain your app can handle, keyed to your Team ID and Bundle ID together (the appID field). Apple checks for it at https://yourdomain.com/.well-known/apple-app-site-associationfirst, and falls back to the domain root if that path doesn't resolve — but the fallback only reliably triggers on a clean 404, not on a redirect or an error page that still returns a 200 status. This is the single most common hosting bug behind "my Universal Links stopped working and I don't know why": a CDN or reverse-proxy rule that redirects unmatched paths, quietly breaking Apple's ability to fall back.
What actually has to match for a Universal Link to open your app
Three things, and a mismatch in any one of them silently degrades the link to an ordinary web URL rather than throwing a visible error: the domain in your AASA file's details entry, the domain in your app's applinks:entitlement, and the App ID (Team ID + Bundle ID) both reference. There's no build-time or runtime error when these don't line up — the link just opens in Safari, and the only way to catch it is to actually test the link on a device with the app installed, not just review the configuration files by eye.
One detail that surprises teams migrating from a shortener service: iOS caches its verification of your AASA file rather than checking it on every single link tap, which means a fix you deploy to the file doesn't propagate to already-installed copies of your app instantly. During testing, reinstalling the app — not just re-tapping the link — is the reliable way to force a fresh check.
Handling the link once it does reach your app is its own step, separate from getting the OS to route it there in the first place. On modern iOS, that means implementing scene(_:continue:) (or the older application(_:continue:restorationHandler:) for apps not yet adopting the scene lifecycle) to receive an NSUserActivity of type NSUserActivityTypeBrowsingWeb, parsing its webpageURL, and routing to the matching in-app screen — including the case where the app launches cold directly into that screen, which behaves differently in testing than a link tapped while the app is already running in the background. Teams that only test the warm-launch case routinely ship a cold-launch bug where the app opens to the home screen and silently drops the intended destination.
Android App Links: assetlinks.json and the Android 15 Change
Android App Links solve the identical problem with a different verification protocol: instead of Apple's AASA format, Android uses the Digital Asset Links standard, hosted as a JSON file at https://yourdomain.com/.well-known/assetlinks.json, paired with an intent filter in your app's manifest carrying android:autoVerify="true".
When your app is installed on Android 6.0 (API 23) or higher, the system inspects every intent filter with a VIEW action, BROWSABLE and DEFAULT categories, and an HTTP or HTTPS scheme, then queries each unique hostname's assetlinks.json to confirm your app is an authorized handler for that domain. Get this wrong — the file missing, wildcarded incorrectly, or hosted behind a redirect — and instead of your app opening automatically, Android falls back to prompting the user to choose an app, or in some cases resolves silently to whichever app was previously selected.
assetlinks.json file can still take up to seven days to propagate to all devices, due to caching and the scheduled re-verification cycle. A domain migration or App ID change planned without that window in mind risks a multi-day gap where links silently stop opening the app for some fraction of your install base.For testing, Android 12 and later expose a manual verification path: adb shell pm verify-app-links --re-verify PACKAGE_NAME followed by adb shell pm get-app-links PACKAGE_NAME to inspect the actual per-domain verification state (values like verified, legacy_failure, or a numeric device-specific error code) rather than inferring it from whether a link happened to open correctly on one test device.
On the client side, receiving the link is handled through onNewIntent() when the app is already running, or by reading the launch Intent's data when the app cold-starts from the link — the same warm-launch-versus-cold-launch split that trips teams up on iOS. A second, Android-specific gotcha: if your manifest declares intent filters for multiple hosts under one activity, every host needs its own matching entry in assetlinks.json, or verification succeeds for some of your domains and silently fails for others — a partial rollout that's easy to miss if your test plan only exercises one hostname.
Custom URL Schemes: The Legacy Path, and Where It's Still Correct
A custom URL scheme — myapp://profile/42 — is the oldest deep-linking mechanism on both platforms, and it works differently from Universal Links and App Links in one important way: there is no domain-ownership verification step at all. Any app can register any scheme it wants; the OS has no built-in way to guarantee your app is the legitimate handler for myapp://, and if a second app registers the identical scheme, which one actually handles a given link is not fully within your control.
That weakness is exactly why custom schemes should not be the primary mechanism for anything a user receives from outside your app in 2026 — a marketing link, a shared post, a push notification payload — where Universal Links or App Links give you the same functional result with actual domain-level verification behind it. What custom schemes still do correctly: OAuth and third-party SDK redirect callbacks that some providers still implement this way, certain share-extension and cross-app-communication patterns, and any purely internal, never-externally-distributed link your own app generates and consumes without ever leaving the device.
The security implications of continuing to use a custom scheme for something more sensitive than internal navigation — specifically an OAuth callback carrying an authorization token — are covered in detail in the security section below; the short version is that a scheme collision there is not a hypothetical bug, it's a documented account-takeover pattern.
What Firebase Dynamic Links' Shutdown Actually Broke
Firebase Dynamic Links (FDL) was, for years, the default answer to "how do I make a short link that opens my app if installed and falls back to a store listing if not." Google announced its deprecation in August 2023, moved the Firebase console to read-only for it in May 2024, and shut it down completely on August 25, 2025. There was no extended grace period after that date and no forwarding service — every link built on FDL's infrastructure, including custom domains routed through it, now returns a plain 404.
page.link or app.goo.gl domain is a cheap, high-value check.The lesson generalizes past this one service: a "free" infrastructure dependency from a major platform vendor is not exempt from being discontinued on that vendor's own schedule, and deep linking specifically has a history of this — Google's separate discontinuation of Play Instant Apps, covered later in this guide, is a second example inside the same eighteen-month window. Native Universal Links and Android App Links don't carry this specific risk in the same way, since they're core platform capabilities rather than a discrete product Google or Apple could sunset independently — but any third-party vendor layered on top of them is still a vendor, with its own continuity risk.
Deferred Deep Linking After App Tracking Transparency
Deferred deep linking solves a specific gap Universal Links and App Links don't cover on their own: routing a brand-new user, who didn't have your app installed at the moment they tapped a link, to the specific content that link pointed to once they finish installing — rather than a generic first-launch home screen. Before 2021, this was commonly solved by passing the device's advertising identifier (IDFA) through the click-to-install flow and matching it server-side once the app opened for the first time — a deterministic, reliable match.
Apple's App Tracking Transparency (ATT) framework made IDFA access opt-in, and the large majority of users decline the prompt — which means deferred deep linking on iOS today mostly runs on probabilistic matching instead: correlating signals like click timestamp proximity, IP address, and device/OS characteristics between the original tap and the subsequent app open, with no single identifier guaranteeing correctness the way IDFA did.
On the number every vendor wants to give you: we searched specifically for an independently audited deferred-deep-link match-rate benchmark and found none. What circulates instead is self-reported vendor marketing content with no disclosed methodology, no stated traffic conditions, and no way to know whether a given percentage reflects a clean single-device-per-household scenario or the much noisier reality of shared Wi-Fi, VPNs, and iCloud Private Relay degrading IP-based signal quality. We refuse to print any of those numbers here as fact — measure your own match rate against your own install funnel instead.
iOS 17 Link Tracking Protection and the Attribution Squeeze
Since iOS 17, Safari strips known tracking parameters from links automatically in Private Browsing — and can be enabled for regular browsing — using Apple's own maintained list of tracking-parameter patterns. The same stripping applies to links opened from Messages and Mail regardless of that setting, which matters directly for deep linking because push notifications, SMS campaigns and email are exactly the channels most deep links travel through.
The mechanism leaves your app's actual destination path intact — a link still opens the right screen — but it can silently remove the click-ID or campaign-tracking parameter your attribution vendor was relying on to tie that specific tap to a specific ad or campaign, before your server or SDK ever receives it. Combined with the IDFA restrictions covered above, this is a second, independent reason deferred-deep-link and attribution matching has shifted toward less parameter-dependent, more probabilistic methods over the past few years — not a preference, a forced adaptation to two separate platform privacy changes landing within a few years of each other.
On the ad-attribution side specifically, Apple's own SKAdNetwork remains operational at version 4.0 (released October 2022), while AdAttributionKit — Apple's newer, longer-term privacy-preserving successor framework — received significant updates at WWDC 2025 and is the direction Apple is steering new attribution integrations toward, without an announced hard cutover date for SKAdNetwork as of when we checked. If your deep-linking and attribution stack touches paid acquisition at all, that migration path is worth scoping alongside the deep-linking work itself, not as a separate project six months later.
Deep Link Security: Where OWASP Meets Intent Filters
Deep link and intent handling maps directly onto categories in OWASP's Mobile Application Security Verification Standard (MASVS) — specifically the controls covering insecure authentication flows and security decisions made on untrusted input. The pattern underneath most real incidents is simple to state and easy to miss in review: a deep link's URL and parameters are input a user (or an attacker) controls, not a trusted internal signal, and code that treats them otherwise is the actual vulnerability.
- Scheme hijacking: Two apps register the identical custom URL scheme; the OS has no ownership check, so which app actually receives a given link isn't fully guaranteed by your app alone.
- Unverified Universal Link / App Link fallback: A missing, wildcarded, or redirect-hosted AASA/assetlinks.json file causes verification to fail silently, and the platform falls back to less secure resolution — a disambiguation dialog on Android, or an ordinary (unverified) web link on iOS.
- OAuth or magic-link callback hijacking: An authorization code or token returns through a deep link that a sibling app can also claim — via a scheme collision or a failed App Links verification — handing that token to the wrong app entirely.
- Unvalidated deep-link parameters used for authorization: A user ID, discount code, or feature flag embedded in a deep link is trusted directly instead of re-validated server-side, letting a forwarded or forged link grant access it shouldn't.
The OAuth case is worth one more layer of detail because it's where deep-link security failures turn into account takeovers rather than annoyances. The standard mitigation, independent of which link mechanism carries the callback, is to generate a random state parameter before starting the authorization flow, store it locally, and reject any callback whose statedoesn't match — that alone defeats a large share of redirect-hijacking attempts, since an attacker's app can receive the callback but won't have the value your app generated. Exchanging an authorization code for a token server-side, rather than trusting a token handed directly in the callback URL, closes the remaining gap: even if a malicious app briefly intercepts the callback, a code alone is not usable without your server's client secret to complete the exchange.
If your broader mobile security posture hasn't had a structured review recently, our mobile app security guide covers the wider OWASP Mobile Top 10 surface this deep-linking-specific section is one slice of.
Reference Architecture and the Order to Build It In
The order matters because each step depends on the one before it, or fails invisibly without it — the same pattern that governs push notification and in-app-purchase infrastructure applies here, with deep linking's specific failure modes swapped in.
| Step | What | Failure Mode If Skipped | Why This Order |
|---|---|---|---|
| 1 | Domain and entitlement setup — AASA + Associated Domains (iOS); assetlinks.json + autoVerify (Android). Hosting-only; no client code yet. | Nothing downstream can verify without this in place first | Both files must exactly match the App ID / package + SHA-256 fingerprint your client will ship — get this right before writing a single line of routing logic. |
| 2 | Client-side link handling — NSUserActivity / scene(_:continue:) on iOS; intent filters + onNewIntent on Android. Parses the incoming URL into a route. | The OS opens your app but nothing happens — the classic 'deep link opens a blank screen' bug | Depends on step 1 being verified, or the OS never routes the link to your app at all. |
| 3 | Route validation and authorization check, server-side or against local session state — never trust the URL's parameters directly. | A forged or replayed link parameter is treated as authoritative — the security gap covered later in this guide | Must exist before step 4, since granting access to content is downstream of confirming the user is allowed to see it. |
| 4 | In-app navigation to the resolved destination — must handle both cold start and already-running app cases. | User lands on the home screen instead of the linked content — the single most common deep-linking complaint in app-store reviews | The step most tutorials treat as the whole feature; in practice it's the last and easiest step once 1–3 are solid. |
| 5 | Deferred deep link matching (if needed) via vendor SDK or custom pipeline — requires install-time SDK initialization. | A new user who tapped a specific product link lands on a generic home screen after installing — a real conversion-rate cost for anything link-driven | Layered on top of 1–4, not a replacement for them; deferred matching only covers the pre-install gap. |
| 6 | Notification and marketing-system integration — every system that generates links (push payloads, email templates, SMS) points at the current routing, not a legacy shortener. | A stale system (an old FDL-based template, a hardcoded shortener) quietly sends dead links for months before anyone notices | Last because it depends on the routing logic above already being correct and stable to point at. |
A note on ownership: association-file hosting, domain verification state, and any legacy shortener migration all need a named owner and a recurring check, not a one-time task closed at launch. A deep-linking setup that worked correctly at launch degrades quietly as infrastructure shifts underneath it — a CDN configuration change that breaks the AASA fallback, a marketing tool that reintroduces a dead shortener domain into a new campaign template — and by the time click-through data shows a drop, the underlying cause has often been live for weeks.
Build vs. Buy: Branch, AppsFlyer, Adjust, and What They Don't Change
This is not a ranked top list, for the same reason a purchase-infrastructure vendor comparison isn't one: native Universal Links and App Links are mandatory platform layers every vendor below sits on top of, so the useful question is what a given vendor actually replaces for you, not which one scores highest on a benchmark nobody can independently verify.
| Layer / Vendor | Category | What You're Actually Buying | Public Security Posture | Corporate Status (checked Sep 10, 2026) |
|---|---|---|---|---|
| Native Universal Links / App Links | Mandatory platform layer | The only OS-level path to link-based app opening; every vendor below sits on top of this, not instead of it | Apple's / Google's own infrastructure; you manage AASA and assetlinks.json hosting and entitlements | Apple/Google platform capability, not a company — not applicable |
| Branch | Deep linking + attribution platform | Deferred deep linking, cross-channel attribution, and link analytics on top of native Universal Links/App Links | Publishes its own security and privacy documentation; verify current certifications directly for a regulated use case | Private, independent; ~$4B valuation as of its last verified major round (Feb 2022); no evidence found of acquisition as of when we checked |
| AppsFlyer | Mobile measurement partner (MMP) + deep linking | Attribution infrastructure spanning deep linking, SKAdNetwork/AdAttributionKit reporting, and fraud protection | Publishes its own compliance and security documentation; verify current certifications directly | Private, independent; raised a $400M debt round in August 2026, per company disclosures |
| Adjust | Mobile measurement partner (MMP) + deep linking | Similar attribution and deep-linking layer to AppsFlyer, now integrated with AppLovin's broader ad-tech stack | Operates under AppLovin's corporate security posture since the acquisition; verify current certifications directly | Not independent — acquired by AppLovin for $1B, deal completed April 2021; operates as a distinct brand within AppLovin |
Building this yourself is defensible when your primary need is straightforward content routing — a notification opens the right product page, a shared link opens the right post — without heavy cross-channel ad-attribution requirements. A vendor earns its fee once ad-spend attribution, a maintained and Apple-compliant deferred-matching pipeline, or cross-campaign link analytics become genuinely necessary; none of them changes what Apple's or Google's platform rules allow, or removes your own obligation to validate deep link parameters server-side rather than trusting them at face value.
What a Broken Deep Link Actually Costs: A Worked Scenario
The following is an illustrative worked scenario, not a real client engagement or a reported outcome — the arithmetic uses realistic, stated assumptions to make the cost of a silent deep-linking failure concrete.
Consider a subscription app sending 200,000 push notifications a month that each deep-link to a specific piece of content — a new episode, a personalized recommendation, a renewal offer. Industry-typical push open rates vary widely by app category and audience, so rather than assume a specific rate, assume this app measures its own baseline at 8% opens, or 16,000 taps a month, and that historically 90% of those taps land the user on the correct in-app screen (some fraction always fails due to edge cases like a cold-start race condition).
| Scenario | Correct-landing rate | Successful deep-link opens per month | Monthly gap vs. baseline |
|---|---|---|---|
| Baseline (working AASA + assetlinks.json) | 90% of 16,000 taps | 14,400 | — |
| After an undetected AASA hosting redirect breaks Universal Links | Falls back to Safari for iOS taps (roughly half the base); Android unaffected | ≈ 10,800 (assuming a 50/50 iOS/Android split) | ≈ 3,600 fewer users land in-app |
| After the redirect is fixed and reinstall-driven cache clears | Returns to 90% over 1–2 weeks as cached verification refreshes | 14,400 (recovering) | Full recovery is not instant — cached client-side verification and, on Android 15+, the up-to-7-day re-verification window both delay it |
The honest reading of that table is not that a broken deep link is a catastrophe on its own — most of those 3,600 users still open the app, just to the wrong screen, and many will navigate manually. The point is that this class of failure is silent by design: nothing throws an error, no crash report fires, and the only visible signal is a click-through or conversion metric drifting down for reasons that look, from a dashboard, indistinguishable from normal noise. The discipline this scenario is meant to illustrate is testing the actual link on a real device after every hosting or CDN change that touches your domain's root or .well-known path — not just reviewing the configuration file by eye.
Red Flags in Vendor and Agency Selection
| Claim | Reality |
|---|---|
| “We'll just use Firebase Dynamic Links, it's free” | FDL shut down completely on August 25, 2025 with no successor service from Google. A vendor or developer proposing it in a 2026 scope has not kept current with a shutdown that's over a year old. |
| A quoted deferred-deep-link "match rate" with no stated traffic conditions | Probabilistic matching accuracy depends heavily on traffic volume, network conditions and threshold tuning; a single headline percentage with no denominator or methodology describes that vendor's aggregate book of business, not your app. |
| No mention of what happens when apple-app-site-association or assetlinks.json verification fails | Both platforms fall back to less secure behavior (a disambiguation dialog, or in some cases a custom-scheme-style resolution) when verification fails silently — a team that hasn't tested and handled that failure mode hasn't actually shipped this feature in production before. |
| "Deep links are safe by default because they're just app navigation" | Deep link parameters are user-controllable input. Treating a link's query string as an implicit authorization signal — especially for an OAuth or magic-link callback — is a documented account-takeover pattern, not a hypothetical. |
| A proposal that never mentions the Android 15 re-verification propagation delay | A domain or App ID change can take up to seven days to fully propagate on Android 15+ per Google's own documentation. A launch or migration plan that doesn't account for that lag risks a broken-links window during a high-visibility rollout. |
If you're evaluating proposals for a broader app rebuild that happens to include deep linking as one piece, our mobile app RFP template guide covers how to write scope language specific enough that competing agencies' "deep linking supported" claims are actually comparable, rather than each vendor claiming the feature at very different levels of rigor.
What This Costs, and Its Limits
| Engagement | Range | Timeline | Typical Scope |
|---|---|---|---|
| Discovery + link-architecture audit | $9k–$22k | 2–4 weeks | URL scheme inventory, AASA/assetlinks.json review, notification payload audit, legacy FDL link discovery |
| Single-platform implementation | $28k–$70k | 4–9 weeks | One platform (iOS Universal Links or Android App Links) built correctly: entitlements, verification testing, graceful fallback |
| Cross-platform build with deferred linking | $70k–$180k | 9–16 weeks | iOS + Android + web routing, deferred deep linking, attribution vendor integration or custom matching pipeline |
| Enterprise / regulated build | $180k–$420k+ | 14–24 weeks | Multi-domain verification, audit logging, documented security review against OWASP MASVS controls |
One scoping note specific to this domain: the discovery phase for deep-linking work should always include a live-link audit — actually tapping a representative sample of links from your current marketing emails, push templates and any printed QR codes on a real device with the app installed, rather than reviewing configuration files alone. This is the fastest way to surface a dead Firebase Dynamic Links domain, a redirect quietly breaking your AASA fallback, or an assetlinks.json mismatch nobody has noticed because nobody has tested the actual link path recently.
None of this replaces your own testing against your specific domain, app configuration and traffic mix — deep linking touches hosting, native app entitlements, and (if you use one) a third-party vendor's own infrastructure simultaneously, and a guide like this one can only describe the current state of three platforms' rules as of the date it was checked, not guarantee they haven't shifted by the time you read it.
Get Your Deep Link Architecture and Attribution Exposure Audited
Book a free 60-minute discovery call with Frenchy Digital, a senior-led Black-owned Los Angeles agency. We review your link infrastructure and attribution setup across platforms and send a written, fixed-price phased proposal within 5 business days.
1517 S Bentley Ave Unit 204, Los Angeles CA 90025
Frequently Asked Questions
Sources & References
- 1Firebase — Dynamic Links Deprecation FAQ↗
- 2Apple Developer Documentation — Allowing Apps and Websites to Link to Your Content↗
- 3Apple Developer Documentation — Supporting Associated Domains↗
- 4Apple Developer Documentation — SKAdNetwork Release Notes↗
- 5Android Developers — Verify Android App Links↗
- 6Android Developers — Google Play Instant↗
- 7Android Developers — InstantApps Package Reference (deprecation notice)↗
- 8OWASP Mobile Application Security — MASVS (Mobile Application Security Verification Standard)↗
- 9OWASP Mobile Application Security — MASTG (Mobile Application Security Testing Guide)↗
- 10OWASP/mastg — GitHub Repository↗
- 11Oversecured Blog — Android Deep Link Vulnerabilities: How Intent Filters Lead to Account Takeover↗
- 12BusinessWire — AppLovin Completes Acquisition of Adjust↗
- 13Paul, Weiss — Adjust Completes $1 Billion Sale to AppLovin↗
- 14TechCrunch — Deep-Linking Startup Branch Is Raising More Than $100M at a Unicorn Valuation↗
- 15PitchBook — Branch (Mountain View) Company Profile↗
- 16AppsFlyer Blog — Firebase Dynamic Links Deprecated: Migrate to Alternatives↗
- 17MacRumors — iOS 17 Can Automatically Remove Tracking Parameters From URLs in Safari, Messages, and Mail↗
- 18Singular — iOS 26: Everything Privacy-Related Apple Announced at WWDC 2025↗
- 19RevenueCat Blog — The Complete Guide to SKAdNetwork for Subscription Apps↗
- 20Android Police — RIP Android Instant Apps, We Hardly Knew You↗

