Android can open https://www.yoursite.com/anything in your app instead of the browser, without asking the user, if two things agree: an intent filter in the app that claims the domain, and an assetlinks.json file on the domain that names the app and its signing certificate. Android fetches the file when the app is installed; if it matches, every link to the domain routes to the app. This is called Android App Links, and it is the feature that makes a converted website feel like it was always an app.
Deep links vs App Links
A deep link is any URL an app says it can handle. If two apps claim the same link, Android shows a chooser — the "Open with" sheet — which is fine for custom schemes and annoying for a website. An App Link is a deep link over HTTPS that has been verified: the domain has published a file saying this app is allowed, so Android skips the chooser. Only the domain owner can publish the file, which is the whole point.
The two halves
In the app: the intent filter
Inside the activity that shows the site, the manifest declares which hosts and paths it opens, with android:autoVerify="true" so Android checks the domain at install:
On the domain: assetlinks.json
Served at exactly https://www.yoursite.com/.well-known/assetlinks.json, it names the package and the SHA-256 fingerprint of the certificate the installed app is signed with:
The generator writes both from your domain, package name and fingerprint, and adds the test commands:
Getting the fingerprint right
This is where most setups fail. The fingerprint must be of the certificate on the installed app. If the app is on Google Play, Play App Signing re-signs it, so the fingerprint you need is the App signing key certificate in Play Console (Test and release → App integrity) — not your upload key. For an APK you distribute yourself, it is your keystore's certificate: keytool -list -v -keystore your.jks. You can list several fingerprints in the file, which is useful for a debug build and a release build side by side.
Hosting the file
- HTTPS, at the exact path, with a
200status — no redirect of any kind, including www ↔ bare and trailing-slash rewrites. Content-Type: application/json. Some static hosts serve.jsonin unknown folders as text.- Reachable by a non-browser client. A firewall or bot challenge that blocks Google's fetcher blocks verification.
- One file per host.
yoursite.comandwww.yoursite.comare different hosts; if both serve the site, both need the file and both go in the intent filter.
Testing
With a phone connected over adb:
Android checks the file once, at install. After fixing a problem, reinstall the app or force a recheck with adb shell pm verify-app-links --re-verify com.yoursite.app. The states you will see are verified, none (no attempt yet) and a numeric error meaning the fetch or the match failed.
Why verification fails
- Upload key instead of app signing key. Nine times out of ten.
- The file redirects. Check with
curl -Ithat the first response is 200. - Wrong content type.
- Only one of www / bare is set up.
- Cached failure. Re-verify after fixing.
Claiming part of a site
Leaving the path off claims every link on the host. If only part of the site lives in the app — the shop but not the blog — add android:pathPrefix="/shop/" and blog links keep opening in the browser. Note that pathPrefix is a plain string prefix: /shop also matches /shopping.
Where the builder stands
The generated app keeps navigation inside the app — links between your pages never leave it — but links tapped elsewhere on the phone open the browser, because App Links are not yet a builder option. This guide and the generator are here so the setup is ready for a native project now and for the builder when the option ships. The iOS equivalent, Universal Links, uses the same idea with an apple-app-site-association file; the iOS guide covers where that fits.