Skip to content
LinkProfit

Deep Links on iOS and Android: A Practical Guide

LinkProfit Team9 min read
  • deep-links
  • developers
  • marketing
On this page

A deep link is a URL that opens a specific screen inside an installed app instead of a web page. That is the whole idea, and it has been implemented three different ways over fifteen years, each with distinct failure modes. The reason marketing teams keep filing bugs about it is that all three still exist simultaneously, and the same URL can behave differently depending on whether it was tapped in a message, a browser, an email client, or an embedded webview inside a social app.

This guide explains the mechanisms honestly, including what they cannot do. If you take one thing away, make it this: routing a click to the right app screen is solved infrastructure, and routing a user to the right screen after they install the app is not, at least not from the link platform alone.

The Three Generations of App Linking

Custom URL schemes

The original mechanism. An app registers a scheme such as myapp, and a URL like myapp://product/42 hands the request to it. Schemes are trivial to implement and still useful as an internal routing format, but they carry two structural problems.

There is no ownership verification: any app can register any scheme, and when two apps claim the same one the resolution is undefined on iOS and a chooser dialog on Android. And there is no fallback: on a device without the app installed, a scheme URL produces an error page or nothing at all, so every scheme link needs a wrapper that detects failure and sends the user somewhere useful. That wrapper is exactly the fragile piece that broke as browsers tightened their handling of navigation timers.

Apple's replacement uses ordinary HTTPS URLs. Your app declares an associated domain entitlement of the form applinks:yourbrand.com, and the domain publishes a JSON file at /.well-known/apple-app-site-association describing which paths belong to the app.

{
  "applinks": {
    "details": [
      {
        "appIDs": ["ABCDE12345.com.yourbrand.app"],
        "components": [{ "/": "/p/*", "comment": "Product pages" }]
      }
    ]
  }
}

The file must be served over HTTPS with no redirects, as JSON, at that exact path. iOS fetches it around install and update time, largely through Apple's CDN, which means changes do not take effect instantly. The benefit is that the same URL works everywhere: if the app is installed and the path matches, it opens; if not, Safari loads the web page. There is no error state.

Android's equivalent uses a Digital Asset Links file at /.well-known/assetlinks.json, naming the app package and the SHA-256 fingerprint of the signing certificate.

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourbrand.app",
      "sha256_cert_fingerprints": ["A1:B2:C3:D4:E5:F6:..."]
    }
  }
]

The app side declares an intent filter for the domain with automatic verification enabled. When verification succeeds, the link opens the app with no chooser dialog. When it fails, the link opens the browser, and the most common cause by far is a fingerprint mismatch: teams publish the fingerprint of their local release key while Play App Signing re-signs the app with a different one. The fingerprint that belongs in the file is the one the store shows for the distributed artifact.

A short link is a decision point rather than a fixed pointer, which is what makes it useful for app campaigns: one printed code can serve iPhone users, Android users and desktop visitors differently, and the decision can change after the code is printed.

The redirect engine sees the request's user agent, the client hints it carries, and network-derived signals such as country. A link configured for app traffic typically holds three destinations: an iOS URL, an Android URL, and a web URL used for everything else. Layered on top, targeting rules can route by country, device, operating system or language, so a campaign can send Canadian Android traffic to one destination and everyone else to another without creating separate links.

Two implementation details are worth knowing because they explain most confusing behaviour.

The first is social crawlers. When a bot such as the Facebook or Telegram fetcher requests a link to build a preview card, a correct redirect engine answers with the preview metadata rather than counting the request as a click and rather than routing it as if it were a phone. If your click counts spike the moment a link is posted and before anyone taps it, the engine is not doing this.

The second is the association boundary, and it is the single most important technical point in this article. Universal Links are evaluated against the domain of the URL the user actually tapped. If someone taps go.yourbrand.com/p/42 and that domain answers with a redirect to yourbrand.com/p/42, iOS does not treat the redirect target as an associated link, so the app does not open. To make a short link open the app natively, the association file must be served on the short domain itself. Where that is not possible, the practical fallback is a handoff to a custom scheme or directly to the store, which is what most shortener deep link features actually do. Ask any vendor which of the two it implements, because the marketing copy is identical either way.

The Fallback Chain

A working app link is really a small decision tree, and each branch needs a deliberate answer.

| Situation | What should happen | Common mistake | | --- | --- | --- | | App installed, path recognised | App opens on the target screen | Redirect chain breaks the association | | App installed, path not recognised | Web page loads, app suggested | Chooser dialog shown on every tap | | App not installed, mobile | Store page for the correct platform | Store link hardcoded to one platform | | Desktop | Full web equivalent of the screen | Landing on a generic homepage | | Social crawler | Preview metadata, no click counted | Preview fetch inflates analytics |

Point the store branch at the platform-appropriate listing rather than a single link, and keep the web branch genuinely equivalent to the app screen. A large share of app-campaign traffic ends up on the web branch no matter how well the rest is configured, and treating it as a dead end wastes most of the spend.

The Install Boundary, Honestly

Here is where vendor marketing and reality diverge. Suppose a new user taps a link to a specific product, does not have the app, lands in the store, installs, and opens it. The app now shows its default home screen, because nothing carried the product identifier across the install. Making that work is called deferred deep linking, and it is not something a link platform can do on its own.

It requires an SDK inside the app that, on first launch, asks a server what the user tapped before installing. The server has to match the two events, and the signals available for that match have narrowed sharply. Device fingerprinting is unreliable and increasingly restricted by platform policy. Advertising identifiers require consent that most users decline. Apple's attribution frameworks report install-level attribution to advertisers rather than passing a path to your app. The clipboard technique that once carried a token now triggers a visible paste notification.

What remains works, but it is a different product with a different integration cost: a mobile measurement partner SDK, in-app initialisation, and a matching window that is probabilistic rather than exact. LinkProfit routes clicks per device and hands off to the store, and it does not claim post-install routing, because doing it properly means shipping code inside your app. If deferred routing is a hard requirement, plan for that SDK next to your link platform rather than instead of it.

Webviews and Other Practical Traps

In-app browsers

Most links tapped inside social apps open in an embedded webview rather than the system browser, and webviews handle app association inconsistently: some ignore Universal Links, some block custom schemes, and behaviour differs across app versions. This is the most common reason a link that works perfectly in testing opens the website in production, since testing usually happens by tapping a link in a message app, which behaves correctly.

There is no configuration that fixes this globally. What works is making the web destination good, adding a visible open-in-app control on it, and testing each publishing channel individually rather than assuming they behave alike.

Association file mistakes

Four recurring ones. Serving the association file through a redirect, including the automatic redirect from the apex to www, which invalidates it. Serving it with the wrong content type or from a path that a framework rewrites. Publishing the wrong Android signing fingerprint, as described above. And forgetting that iOS caches the file, so a correction can take a day or more to reach devices that already have the app installed.

Analytics on the app branch

Once a click enters an app, your web analytics stop seeing it, and this is where reporting quietly falls apart. Keep the campaign parameters on the link so the redirect engine records them, and pass an identifier through to the app destination so the in-app session can be joined to the click. Our guide to tracking link clicks covers what a click event can and cannot tell you, and the same caveats about bots and unique visitors apply here.

Testing before publishing

curl -sSI https://yourbrand.com/.well-known/apple-app-site-association
curl -sS https://yourbrand.com/.well-known/assetlinks.json

# Android: open a URL as if it were tapped, then inspect verification state
adb shell am start -a android.intent.action.VIEW -d "https://yourbrand.com/p/42"
adb shell pm get-app-links com.yourbrand.app

# iOS simulator
xcrun simctl openurl booted "https://yourbrand.com/p/42"

The first two commands should return 200 with no redirect. Note that typing a Universal Link into Safari's address bar deliberately does not open the app, so test from a note, a message, or the simulator command instead, or you will chase a bug that does not exist.

Deep link support is packaged very differently across the category, and the differences are about which plan you must buy rather than about capability.

| Vendor | Deep link availability, as of August 2026 | | --- | --- | | BL.INK | All plans, entry tier 48 USD per month for one user and one domain | | Short.io | From the Team plan at 48 USD per month | | Rebrandly | From the Growth plan at 99 to 119 USD per month | | Switchy | Advertised coverage of more than 130 apps | | LinkProfit | Per-device destinations on all plans |

BL.INK is the honest benchmark here: including deep links on every tier is unusual, even though the entry price is high for a single domain. Rebrandly's placement on Growth is the pattern to watch for generally, since the tier you need for one feature tends to determine the whole bill.

A Deployment Sequence That Works

  1. Decide the canonical web URL for each app screen you want to link to; the web page is the fallback and the source of truth.
  2. Publish both association files on the domain that will appear in the links, and verify them over HTTPS with no redirects.
  3. Add the associated domain entitlement on iOS and the verified intent filter on Android, using the fingerprint of the distributed build.
  4. Confirm which domain your short links will be tapped on, and whether the association lives there or the platform hands off to a scheme or store.
  5. Configure per-device destinations plus a web fallback on each link, with the correct store listing per platform.
  6. Test on both platforms from a message app, from the system browser, and from every social app you publish in.
  7. Verify that campaign parameters survive the redirect and are recorded in analytics.
  8. Automate creation through the API if links are generated per campaign, per recipient or per product.

None of this is difficult in isolation. The difficulty is that the pieces live in three places, the app project, the DNS zone, and the link platform, usually owned by three different people. Writing down which one owns the association file is worth more than any individual configuration tip in this article.

Questions people ask

What is the difference between a deep link and a Universal Link?

Deep link is the general idea: a URL that opens a specific screen inside an app rather than a website. Universal Links on iOS and App Links on Android are the modern implementations of that idea using ordinary HTTPS URLs, verified by a file hosted on your domain. Custom URL schemes such as myapp://product/42 are the older implementation: they still work, but any app can claim a scheme, and a device without the app installed shows an error rather than a fallback.

Do short links break Universal Links?

They can, and this is the most common surprise in the category. iOS evaluates the association against the domain of the URL that was actually tapped, so a short domain that redirects to your app domain may hand the request to the browser instead of the app. Two workarounds exist: host the association file on the short domain itself so the short link is the associated URL, or accept a redirect-based handoff to a custom scheme or store page. Ask your vendor which of the two it implements before you standardise on short links for app campaigns.

Can a short link send a new user to the exact screen after they install the app?

Only with help from inside the app. Routing that survives an install, usually called deferred deep linking, requires an SDK in the app that asks a server what the user tapped before installing, and the matching signals available to that server have narrowed considerably on iOS. Platform-side link routing alone cannot do it: the redirect ends at the store, and the store does not pass your path through. If post-install routing is a core requirement, budget for a mobile measurement partner SDK alongside your link platform.

Why does my link open the website instead of the app inside Instagram or TikTok?

Links tapped inside social apps usually open in an embedded webview rather than the system browser, and webviews handle association inconsistently. Some ignore Universal Links entirely, some block custom schemes, and behaviour varies by app version and platform. The practical answer is not to fight it: make the web destination genuinely usable, put a visible open-in-app control on that page, and test each channel you actually publish in rather than assuming parity.

Which link platforms include deep links on entry plans?

BL.INK is the outlier: deep links are available on every plan, although its entry tier starts at 48 USD per month for one user and one domain, as of August 2026. Rebrandly puts deep links behind its Growth tier at 99 to 119 USD per month, Short.io from its Team plan at 48 USD, and Switchy advertises coverage of more than 130 apps. LinkProfit includes per-device destinations on all plans.

How do I test deep links without publishing a campaign?

Use the platform tools directly. On iOS, xcrun simctl openurl booted opens a URL in a simulator, and a note or message on a physical device tests the real association path, since typing a URL into Safari deliberately does not trigger Universal Links. On Android, adb shell am start with the VIEW action opens a URL, and the pm verify-app-links commands report the verification state. Also fetch both association files with curl and confirm they return 200 with no redirect.