Deep linking
Deep links, universal links and app links explained
Four terms, constantly used interchangeably, that behave very differently in practice. Here is the distinction that matters — and the one rule that keeps deep links from breaking.
A deep link is a URL that points at a specific place inside an app rather than at its home screen — a particular album, product, chat or profile. That is the whole concept. The confusion comes from the four different technologies that implement it, each with different failure modes.
Custom URL schemes
The oldest mechanism. An app registers a scheme like spotify: or myapp:, and any URL starting with it gets handed to that app.
- Upside: simple, works on both platforms, no server-side setup at all.
- Downside: if the app is not installed, nothing happens. The tap produces an error dialog or, worse, silence.
- Downside: schemes are not owned or verified by anyone. Two apps can claim the same one, and the OS picks a winner arbitrarily.
- Downside: most in-app browsers block scheme navigation outright, which is precisely the context social traffic arrives in.
Custom schemes are still useful as a last attempt, but building a link strategy on them alone means accepting a meaningful share of taps that go nowhere.
iOS universal links
Apple's answer to the problems above. A universal link is an ordinary https:// URL. If the matching app is installed it opens the app at that location; if it is not, the same URL loads as a normal web page.
Making one work requires real setup on the domain: an apple-app-site-association file served over HTTPS from the site root, the Associated Domains entitlement in the app, and matching path configuration. That setup is what makes it verified — no other app can hijack your URLs.
Android app links
The Android equivalent, with the same shape: an HTTPS URL, verified against the domain via an assetlinks.json file, plus matching intent filters in the app manifest. Verified app links open the app directly with no disambiguation dialog.
Android also has intent URLs (intent://…#Intent;…;end), a more explicit format that can name a target package and — critically — carry a browser_fallback_url. That fallback is why intent URLs are the most reliable deep-link format available on Android: if the app is missing or the launch is blocked, the browser goes to the fallback URL instead of failing.
Side by side
| Custom scheme | iOS universal link | Android app link | Android intent URL | |
|---|---|---|---|---|
| Looks like | app://path | https://… | https://… | intent://…#Intent;…;end |
| Domain setup needed | No | Yes (AASA file) | Yes (assetlinks.json) | No |
| Verified ownership | No | Yes | Yes | No |
| If app not installed | Fails silently or errors | Loads the web page | Loads the web page | Goes to fallback URL |
| Survives an in-app browser | Often blocked | Sometimes | Sometimes | Usually |
The rule that actually keeps deep links working
Never treat a native launch as guaranteed. The app may not be installed, the scheme may have changed in an update, the webview may block the navigation, or the user may be on desktop. Every one of those is normal, not exceptional.
So the correct structure is a chain, not a destination:
- 1Attempt the native target — but only where it makes sense (mobile OS, a known app for this destination).
- 2Fall back to the web URL for that same content, so the visitor lands where they meant to go regardless.
- 3Never confirm success from the tap alone. A click is not proof the app opened. Only an observable follow-up event is.
That last point catches a lot of teams out. There is no reliable callback telling a web page "the app launched successfully", so any analytics that count taps as app opens are overstating the number.
How OpenOut handles it
Each link has a routing mode. In smart and native_app modes on a mobile device, OpenOut resolves the destination against a catalog of known app targets — matching the destination's host, then building the right platform-specific link — and constructs the fallback chain described above, with the web URL as the guaranteed endpoint. Other modes (standard, external_browser, guide) skip the native attempt entirely.
Every candidate URL is validated before it is used: only http(s) and known-safe app schemes pass, and the destination host must belong to the link. That check exists because a deep-link resolver that will follow any URL handed to it is an open redirect, and open redirects get abused for phishing.
The deep links page covers the routing modes in more detail.
Frequently asked questions
What is the difference between a deep link and a universal link?
"Deep link" is the general concept — a URL pointing at a specific location inside an app. A universal link is Apple's specific implementation of it using verified HTTPS URLs. Every universal link is a deep link; not every deep link is a universal link.
Do deep links work if the app is not installed?
It depends on the type. Custom URL schemes typically fail. Universal links and Android app links fall back to loading the equivalent web page. Android intent URLs go to whatever browser_fallback_url you specify. This is exactly why the fallback chain matters.
Do deep links work inside TikTok's or Instagram's in-app browser?
Inconsistently. Webviews often block custom-scheme navigation, and behaviour varies by platform and OS version. Android intent URLs are the most likely to work. Always assume the attempt can fail and keep a web fallback.
Can I track whether the app actually opened?
Not reliably from a web page. There is no dependable success callback, so click counts overstate app opens. Meaningful measurement has to come from an event on the other side — inside the app or on the destination page.