Glossary/Implementation artifacts
Link Fallback Chain
Definition
A link fallback chain is the ordered set of destinations a single link resolves to — the app, the app store, or a web page — depending on the user's platform and whether the app is installed.
One URL goes into an ad, an email and a QR code, and it has to do something sensible for a user on iOS with the app, a user on Android without it, and a user on a desktop browser. The chain is the routing logic that decides that. Get it right and every audience lands somewhere useful; get it wrong and a large share of your traffic hits an App Store page for the wrong platform, or a blank screen.
The decision table
Every branch of the chain is a combination of platform, install state and context. There are fewer of them than it feels like.
| Platform | App installed | Destination | Mechanism |
|---|---|---|---|
| iOS | Yes | The app, on the right screen | Universal Link |
| iOS | No | App Store, then the right screen after install | Web page → App Store → deferred deep link |
| Android | Yes | The app, on the right screen | App Link |
| Android | No | Play Store, then the right screen after install | Web → Play with a referrer → Play Install Referrer |
| Desktop | n/a | The web page, or a QR code to continue on mobile | Plain HTTP |
| In-app browser | Yes | The app, if the webview can be escaped | intent:// on Android; interstitial on iOS |
| Unsupported / bot | n/a | The web page | Plain HTTP |
The store is not the last hop
Sending an uninstalled user to the store and stopping there loses the content they clicked. The chain only pays off if the app, on first launch, recovers what the link pointed at — the Play Install Referrer on Android, a matched deferred link on iOS. Without that step, a fallback chain is a redirect to a home screen.
Where the decision has to be made
It has to happen on the server, in the redirect. Client-side detection is the approach every guide from 2015 describes, and it does not survive current browsers.
| Client-side timer | Server-side redirect | |
|---|---|---|
| How it works | Open the scheme, setTimeout to the store | Inspect the request, respond with a 302 |
| Blocked by modern browsers | Yes — scheme navigation needs a gesture | No |
| Survives an in-app browser | Rarely | Usually |
| Shows an error dialog when the app is missing | Often | Never |
| Can attribute the click | Only after the page loads | At the redirect, before anything renders |
| Works with a Universal Link | Conflicts with it | Complements it |
# iOS: the link is a plain HTTPS URL. If the app is installed the
# OS intercepts before any request is made; otherwise a 302 to the store.
curl -sSIL -A "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)" \
https://links.example.com/p/abc123
# Android: same URL, different Location
curl -sSIL -A "Mozilla/5.0 (Linux; Android 14)" \
https://links.example.com/p/abc123
# Desktop: the web page, 200, no store redirect
curl -sSIL -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \
https://links.example.com/p/abc123Run those three and read the Location headers. Any user agent that lands on the wrong platform's store, or on a store when it should have loaded the page, is a branch of the chain that is wrong for real traffic too.
The Android escape hatch
Android has a URL form that expresses the whole chain in one string: try this app, and if it is not there, load this web page. It is the only reliable way out of an in-app browser.
<a href="intent://products/42#Intent;
scheme=https;
package=com.example.shop;
S.browser_fallback_url=https%3A%2F%2Fexample.com%2Fproducts%2F42;
end">
Open in the app
</a>
<!-- Newlines shown for readability; write it on one line.
S.browser_fallback_url must be percent-encoded. -->iOS has no equivalent
There is no iOS URL form that says "app if present, web if not" — that is what a Universal Link is, and it is why iOS chains rely on the association working rather than on the URL. Where an iOS webview blocks Universal Links, the only remaining option is an interstitial page asking the user to open in Safari.
Preserving context across the store
The hard part of the chain is the segment that crosses an install, because nothing about the original URL is carried by the store automatically. Each platform gives you exactly one channel.
| Platform | Channel | Survives | Limits |
|---|---|---|---|
| Android | referrer parameter on the Play URL, read via the Install Referrer API | Deterministic, exact | Play installs only; read within the API's retention window |
| iOS | A match performed by your attribution provider on first launch | Probabilistic in the general case | Time-limited; no device identifier is involved |
| iOS App Clip | The invocation URL, carried into the full app via a shared container | Deterministic | Only when the user came through an App Clip |
| Both | A code the user types or a QR the user rescans | Deterministic | Requires the user to do something |
# The store URL a fallback chain should redirect an uninstalled
# Android user to. Everything after referrer= is yours to define.
https://play.google.com/store/apps/details\
?id=com.example.shop\
&referrer=utm_source%3Demail%26utm_campaign%3Dspring%26deep_link_value%3D%2Fproducts%2F42
# Confirm the app reads it back on first launch
adb shell setprop debug.deeplink.referrer "deep_link_value=/products/42"The referrer value has a length limit and is a single opaque string, so keep it to a compact encoding of the route plus campaign fields rather than a full URL. When it comes back empty, the causes are in deferred-deep-link-not-working.
UTM builder
Build the tagged campaign URL that sits at the front of the chain, with consistent utm_source, utm_medium and utm_campaign values. Consistent tagging is what makes the store-referrer segment readable later, since that string is the only context that survives an install on Android.
Frequently asked questions
- What is a link fallback chain?
- It is the ordered routing logic behind a single link: open the app if it is installed, send the user to the correct app store if it is not, and load a web page for anyone on a platform with no app. A complete chain also recovers the original destination after an install, so a user who had to install first still lands on the content they clicked.
- Should fallback logic run on the client or the server?
- On the server, as a redirect. Current browsers block navigation to a custom scheme without a direct user gesture, and the old technique of opening a scheme and racing a setTimeout to the store fails or shows an error dialog on most devices. A server-side redirect inspects the request and returns the right destination before anything renders.
- How do I send an Android user to the Play Store with the link intact?
- Redirect to the Play Store URL with a referrer query parameter containing your campaign and destination fields, percent-encoded. Google Play passes that string to the app, which reads it on first launch through the Install Referrer API. It is deterministic and exact, which makes it the most reliable segment of any fallback chain on either platform.
- What is S.browser_fallback_url?
- It is a parameter inside an Android intent:// URL that names a web page to load when the target app is not installed. It lets a single link express the whole fallback decision in one string, and it is the most reliable way to open an app from inside an in-app browser such as Instagram or TikTok, where standard App Links are frequently intercepted.
- Why do users end up on the wrong app store?
- Because platform detection is running on a user agent the chain does not recognise, or not running at all. In-app browsers, tablets and desktop browsers all report user agents that naive matching misclassifies. Test the chain with curl against several user agent strings and read the Location header for each, rather than testing only on your own phone.
Related terms
- Universal Link — A Universal Link is a standard HTTPS URL that opens an iOS app directly when that app is installed and the domain has authorised it, and loads the equivalent web page when it is not.
- Android App Link — An Android App Link is an HTTPS URL that opens an Android app directly, without a chooser dialog, because the system has verified through a file on the domain that the app is authorised to handle it.
- Custom URI Scheme — A custom URI scheme is a non-standard URL protocol, such as myapp://, that an app registers with the operating system so that URLs beginning with it open that app.
- Deferred deep link not working — A deferred deep link fails when the signal meant to carry the pre-install destination across the app store — an install referrer, a stored token, or a server-side match — is absent, expired, or never read on first launch.
- Deep links in in-app browsers — An in-app browser is an embedded webview inside another app that renders links without handing them to the operating system, which prevents Universal Links and App Links from ever reaching the app that claims the domain.