What Deep Linking Is, and How to Check That a Link Opens the App
Two files on your website decide whether a tap reaches your app or the browser. See what Apple and Android fetch, when they check, and how to read it.
A deep link is a web address that opens one named screen inside a mobile app instead of the app's front page, or the matching page in a browser when the app is not there.
The phrase carries a second, older meaning: a link pointing at an interior page of a website rather than its home page.
Everything below is about the app sense.
Whether a deep link works is not settled inside the app. A file published on your website settles it, together with whether the platform has fetched that file and agreed the app is yours.
What a Deep Link Is, and the Second Meaning of the Same Phrase
A deep link carries someone past the app's front door to one named place inside it. Google's Search Central team, writing in May 2025, defines deep links as "special URIs that take users beyond your mobile app's homepage, leading them directly to specific in-app content".
The example is easier than the definition. Say a shoe retailer emails a customer about one pair.
Tap that link with the app installed and the app opens on the pair. Tap it without the app and the same address opens the product page in a browser.
One address, two destinations, and no choice handed to the reader.
The older meaning is the one Wikipedia's encyclopedia entry still opens on: a hyperlink pointing to a specific indexed page on a website rather than to its home page. That sense produced a run of court cases in the late 1990s.
The mobile meaning is the live one, and it is the harder one.
A web link works the moment you publish it, and an app link does not.
The Difference Between a URL and a Deep Link
A deep link is a URL. Nothing in the address itself makes it deep.
What makes it a deep link is a claim made elsewhere. An app has told the operating system that it handles addresses on that domain, and it knows which screen each path means.
So the difference sits in what has been registered about the domain, not in the link. Hand two phones the same address, https://shop.example.com/shoes/8412, and they can behave differently, depending on what each one has installed and approved.
That is why a link that "does not work" is often not a broken link.
The address is fine. The claim behind it was never accepted.
Matching is not the same as routing. Android's documentation says that even though your intent filters match a given address, there is no guarantee the system will route the link to your app.
What Happens Between the Tap and the Screen
Between the tap and the screen, the operating system makes one decision, and it makes that decision before your app is involved. Android publishes the decision as an ordered list.
Android's documentation names the order plainly. Open the user's default app for the address if one was designated, otherwise open the only available app that can handle it, otherwise show a disambiguation dialog and let the user choose.
Read that order backwards and the design goal is clear.
A verified link never reaches the third step, because the system already knows whose address it is.
When the App Is Already Installed
With the app installed and the domain approved, the tap goes straight to the screen the path names. Nothing appears in between, no chooser and no browser.
That is the whole promise, and it holds only while the approval holds. The same tap on a phone where the domain was never verified takes a different branch.
On Android 12 and higher, the difference is invisible to the person tapping. Android's documentation states that a generic web intent resolves to an activity in your app only if the app is approved for that domain.
If it is not approved, the intent resolves to the user's default browser app instead.
No dialog, no error, no signal that anything else was meant to happen. The link opens a web page, and everyone assumes that was the plan.
When the App Is Not Installed
Without the app, an ordinary web address still works, because it is an ordinary web address.
The browser opens the page and the visit continues.
A custom scheme address has no such fallback. Nothing on the phone claims it, so the tap fails or lands on an error, which is one reason plain web addresses became the sensible default.
The harder case is the person who taps, installs the app, and opens it for the first time on a blank home screen. Carrying them to the screen they asked for takes a separate mechanism.
Four Ways a Link Can Reach an App Screen
Four routes reach an app screen, and only one of them is verified. Android's documentation names the first three as its supported types, and the fourth exists because the first three all assume the app is already there.
The four are a custom scheme address, a plain web address, a verified web address, and a deferred link that survives an install.
Custom Uniform Resource Identifier (URI) Schemes, and Why They Stopped Being Enough
A custom scheme is an address in a made-up protocol that an app claims for itself, in the shape example://products/123. Android's documentation gives that exact form.
They still work, and they remain useful for navigation inside software you control. What they cannot do is prove who owns them.
Android's documentation says a custom scheme is not a standard web link, and that it can still trigger the disambiguation dialog if another app registers the same custom scheme.
Mohanty and Thiyagarajan, writing in the Journal of Computing and Intelligent Systems in 2023, state the security consequence directly: "any other app or malicious app can register the same scheme and trick you into opening their app."
Their next sentence is why the platforms moved. "As a result, they can steal your information."
Their paper dates the fix, putting the intent URL at 2013 and the App Link at 2015.
Plain Web Addresses, and What Android 12 Changed
A plain web address uses http or https and works everywhere, which is what makes it versatile and what leaves it unclaimed.
Android's documentation dates the change. On Android 12 and higher, web links that are not verified App Links generally open in a web browser by default.
On previous versions a disambiguation dialog might have appeared instead, if an app could handle the link.
An unverified web address is therefore a working web page and a broken app link at the same time, and nothing on the screen tells you which one you are looking at.
Verified Links: Apple's Universal Links and Android's App Links
A verified link is a plain web address that the platform has confirmed belongs to your app. Apple calls the result Universal Links, and Android calls it App Links.
Android's documentation describes App Links as verified web links, available since Android 6.0. Once verified, the system routes links for that domain directly to your app, skipping the disambiguation dialog entirely.
The confirmation is a file on your website, and it is the only part of the mechanism that does not live in the app.
Android adds one condition that is easy to miss. App Links is supported on Android 6 and later, on devices that have Google services.
Deferred Deep Links, for People Who Do Not Have the App Yet
A deferred deep link holds the destination across the install. Someone taps, lands in the app store, installs, opens the app, and arrives at the screen the original link named rather than the home screen.
Neither platform provides this on its own, because the address is gone by the time the new app first runs. Holding it takes a third-party service that matches the click to the install.
Google used to offer one and stopped.
Firebase Dynamic Links shut down on August 25th, 2025, the date Firebase's own support page on Dynamic Links names.
Every link clicked after that returns a 404 status response to the person who tapped it. For an app that is already installed, the same page points at App Links and Universal Links as the platform-supported way to do this.
A link that also carries information about where the click came from is sometimes called a contextual deep link. It is the same mechanism with campaign data attached.
Which of the Four to Choose
Choosing between the four is mostly settled by where the link will be tapped, and by whether the person tapping already has the app.
A link that leaves your control, in an email, an advertisement or a social post, has to be a verified web address. Nothing else survives contact with a phone that does not have your app.
A custom scheme is the right tool inside software you own, where no stranger can register the same scheme against you.
A deferred link is for the case where acquiring the app is part of the point, and it is the only one of the four that costs you a third-party dependency.
The Two Files That Decide Whether Any of It Works
Two files decide whether any of this works, one for each platform, and both live on your website rather than in the app. The app names a domain, the domain names the app back, and the platform checks that the two agree.
Apple's file is called apple-app-site-association and has no extension. Android's is called assetlinks.json.
Both are served from the /.well-known/ directory at the root of the domain.
Apple publishes three hosting rules, and each is a way to fail. You must host the file using https:// with a valid certificate and with no redirects.
Android's documentation states the address it queries rather than the rules, and that address is also an https one under /.well-known/.
Where the two platforms genuinely differ is timing, and that difference decides how long you wait to find out whether a change worked.
What Apple Fetches, and When
Apple's phones no longer ask your web server for the file. Apple's documentation says that starting with macOS 11 and iOS 14, apps no longer send requests for apple-app-site-association files directly to your web server.
They send those requests instead to an Apple-managed content delivery network (CDN) dedicated to associated domains.
That single change explains most of the confusion about why an edit did not take effect. Your server logs do not show the phones, because the phones are not asking your server.
Apple publishes both intervals. Its content delivery network requests the file for your domain within 24 hours, and devices check for updates approximately once per week after app installation.
So a corrected file is not live when you upload it.
It is live when Apple has fetched it and the phone has next looked, and neither event is yours to trigger.
There is one escape hatch, and it is for development only. Apple's alternate mode lets an app bypass the network and connect directly to a private domain while the web server is unreachable from the public internet.
Subdomains do not come free either.
Apple's documentation says a site using several subdomains needs a separate entitlement entry for each one, and each must serve its own association file.
What Android Checks, and When
Android verifies on install, and it tells you how long to wait. Its documentation says that when android:autoVerify="true" is present in at least one of the app's intent filters, installing the app triggers verification automatically.
That holds on any device running Android 6.0 or higher. The system then queries each hostname for the Digital Asset Links file at https://hostname/.well-known/assetlinks.json.
Verification is asynchronous, which is why the documentation gives a number. Wait at least 20 seconds for the process to complete before checking the result.
One older behavior is worth knowing if you own several domains. On Android 11 and lower, the system makes your app the default handler only if it finds a matching file for every host in the manifest.
So on those versions one broken domain took the rest down with it.
A corrected file has two different answers on Android, and the split is the Android version.
Android's documentation says that on Android 14 and lower the system does not perform periodic background re-verification, and that updates to the file are typically only picked up when the app is installed or updated.
On Android 15 and higher the system re-verifies domains in the background, and Android's documentation says changes can take up to seven days to propagate to all end-user devices.
So on the older versions the wait is not a wait. Nothing lands until the app is installed again.

Use this chart — embed code and citation
<a href="https://neerajjivnani.com/blog/deep-linking/"><img src="https://neerajjivnani.com/infographics/deep-linking/two-clocks.png" alt="Diagram comparing when each mobile platform reads the association file it verifies a deep link against, showing Apple's content delivery network requesting the apple-app-site-association file for a domain within 24 hours and devices re-checking approximately once per week after app installation, beside Android's assetlinks.json being queried on install and verified asynchronously with a documented wait of at least 20 seconds, and then the separate clock for a file that changes later, where Android 15 and higher re-verifies in the background with changes taking up to seven days to propagate while Android 14 and lower picks up an update only when the app is installed or updated, with the shared hosting rules of HTTPS, a valid certificate and no redirects named once for both." width="1200"></a>
<p>Chart: <a href="https://neerajjivnani.com/blog/deep-linking/">Neeraj Jivnani</a></p>Neeraj Jivnani, "What Deep Linking Is, and How to Check That a Link Opens the App", neerajjivnani.com, https://neerajjivnani.com/blog/deep-linking/Free to republish with a link back to this page.
How to Tell Whether a Link Is Verified
Android will tell you whether a link is verified, and it takes one command. Running adb shell pm get-app-links followed by the package name returns a domain verification state for every domain the app claims.
The state is the answer. Android's documentation says domains that successfully pass verification have a state of verified, and that any other state indicates the verification could not be performed.
Android 17 adds a second diagnostic. Its documentation describes a --debug-link flag for am start that reports how the system resolved an address and which intent filters matched.
Both of those need a developer and a connected handset. Google names one check that needs neither, in its Search Central post: a deep links page in the Play Developer Console.
That page gives an overview of your existing setup, reports on advertisement addresses which are not deep linked, and publishes fixes without needing a new app release.
The Nine States, and the One That Counts
Nine values are documented, and only the first row below means the file was fetched and accepted.
| state | what Android says it means | what it tells you |
|---|---|---|
verified | The domain is successfully verified for the declaring app | The file was fetched and accepted. This is the only state that means the mechanism worked |
none | Nothing has been recorded for this domain | Not a failure yet. Android says to wait a few more minutes and run the verification again |
approved | The domain was force-approved, usually by executing a shell command | The app handles the domain on this device only, because someone set it by hand. Your file was not what did it |
denied | The domain was force-denied, usually by executing a shell command | Set by hand in the other direction, so a test on this device proves nothing |
migrated | The system preserved the result of a previous process that used legacy domain verification | Carried over from the old verifier rather than confirmed by the current one |
restored | The domain was approved after the user performed a data restore. It's assumed that the domain was previously verified | Approval survived a device restore on an assumption, not on a fresh check |
legacy_failure | The domain was rejected by a legacy verifier. The specific failure reason is unknown | A rejection with no diagnosis attached, so the file has to be checked by hand |
system_configured | The domain was approved automatically by the device configuration | The device decided, usually a preloaded app on a particular build |
| error code 1024 or greater | Custom error code that's specific to the device's verifier | Android says to confirm the network connection and run the verification again |
Three of the eight that are not verified read like success, because Android's own gloss for each of them says the domain was approved. They are approved, restored and system_configured.
A domain sitting at any of those three will open your app on the phone in front of you and may open nothing on a customer's.
Checking the Apple Side
Apple's associated domains documentation names no equivalent command, so the check on that side is the file rather than the state. Fetch it over https from the /.well-known/ path with a browser or a plain request.
Confirm that it comes back as a readable document, and that nothing redirected on the way.
Then wait, because Apple's own clock decides when a change takes effect.

Use this chart — embed code and citation
<a href="https://neerajjivnani.com/blog/deep-linking/"><img src="https://neerajjivnani.com/infographics/deep-linking/nine-states.png" alt="Reference card of the nine domain verification states that Android's pm get-app-links command reports for an App Link, listing verified, none, approved, denied, migrated, restored, legacy_failure, system_configured and error codes of 1024 or greater with Android's own definition of each, marking verified as the single state that means the association file was fetched and accepted, and flagging approved, restored and system_configured as the three states that read like success while meaning the file was never what granted the approval." width="1200"></a>
<p>Chart: <a href="https://neerajjivnani.com/blog/deep-linking/">Neeraj Jivnani</a></p>Neeraj Jivnani, "What Deep Linking Is, and How to Check That a Link Opens the App", neerajjivnani.com, https://neerajjivnani.com/blog/deep-linking/Free to republish with a link back to this page.
Why a Link That Looks Right Still Opens the Browser
A link that looks right still opens the browser for five reasons. Two are broken hosting rules, one is a clock, one is the phone you happen to be testing on, and one is where the link was tapped.
Take them in the order they break, because the first two are settled before anything is verified at all.
- A redirect on the `/.well-known/` path. Apple's rule allows no redirects, so a rewrite that quietly sends the request somewhere tidier fails the fetch. Content management systems and edge configurations add these without being asked.
- The wrong content type or a login in the way. The file has to come back as a plain readable document to an anonymous request. Anything that returns a page instead of the file, including a security challenge, ends the check.
- A stale file the platform has not re-read. The edit is correct and the answer is still wrong, because Apple's network fetches within 24 hours and the device looks about once a week, and Android 15 takes up to seven days. On Android 14 and lower nothing re-reads it until the app is installed again, so waiting never resolves it.
- A test on the wrong device state. A domain reading
approvedorrestoredon the test phone was not verified by your file, so the test confirms the phone rather than the setup. - A link opened inside another app. A tap in a mail client or a social feed can be handled by that app's own browser, which never hands the address to the operating system, so the routing you configured is never consulted.
Why the Failure Goes Unreported
The pattern behind all five is worth naming on its own. Deep linking fails silently by design, because the fallback is a working web page.
A working web page looks like a success to everyone who is not checking the state. Nobody files a complaint about a link that opened the site.
That is what separates this from an ordinary bug. An ordinary bug produces a report, and this one produces a slightly worse conversion rate that gets attributed to something else.
Blocked, or waiting
A deep link that does not open the app is in one of two conditions, and they need opposite responses. Either a published rule is in the way, which is yours to change today, or a platform clock is still running, which nothing you can run will shorten. Answer for your own setup and the two come apart.
The phone you are testing on
Where you tapped the link
The file on your website
- The file is served over https:// with a valid certificate
- Nothing redirects on the /.well-known/ path
- It comes back as a plain readable document to an anonymous request
Which of the two you are in
Say which phone you are testing on
Apple publishes the hosting rules and both platforms publish a clock, so the same broken link reads differently on an iPhone and on an Android handset. Nothing is scored here, and nothing is graded. Two lists come back: what is blocking, and what is still running.
What a Deep Link Is Worth to a Campaign, and What It Is Not
A deep link is worth two or three clicks to a campaign, and Google has published that figure itself. Its Search Central post from May 2025 says landing users directly on in-app content saves them an average of 2-3 clicks.
The payoff Google names is time: fewer clicks to reach the content means more of the visit is spent in the app.
That is a modest claim, and it is the honest one. That saving compounds where campaigns live, because one address can sit in an email, an advertisement, a social post or a referral program and behave correctly in all of them.
Carrying campaign information in the link is what makes the arrival measurable.
The path names the destination, the query string names the source, and the app can read both.
Google reports one side of this for you. Its post notes that Search Console includes performance for a site's Android app deep links, under the Android App search appearance filter.
What a Deep Link Does Not Buy
Deep linking does not change how your pages rank in search, and Google's Search Central post says so outright. Search continues to use the content of your web pages for indexing and ranking.
A condition follows from that, and Google states it as one. Add deep links only where the app page carries the same content as the corresponding web page.
Otherwise the title and snippet shown in Search could mislead people about what they will see after the click.
Layout differences are fine, as long as the content matches.
So a deep link improves the arrival and leaves the acquisition alone. Anyone selling it as a ranking tactic has read something else.
The Decisions That Sit With Marketing, Not Engineering
Four decisions here are not engineering decisions, and they are the ones that stall a deep linking project. Engineering can build any of the four answers and cannot choose between them.
- Which addresses matter. Deep linking every page is rarely the goal, and the honest list is usually the handful of destinations campaigns point at.
- What happens with no app installed. The web page is one answer and the app store is the other, and one of them costs you the visit you already had.
- The content match Google imposes. Deciding that an app screen and a web page carry the same content is an editorial judgment, made page by page.
- Who owns the file. The file lives on the website, so the web team holds it, while the thing it breaks belongs to the app team, and the failure is invisible to both.
That last one is why these projects go quiet.
The check is cheap and the state is published. Run it after every app release, and after any change on the website that could touch the file, such as a new redirect rule or a certificate renewal.