Deep Linking Explained: Universal Links and App Links Step by Step

Last updated: ⏱ Reading time: ~19 minutes

AI-assisted guide Curated by Norbert Sowinski

Share this guide:

Diagram-style illustration of mobile deep linking showing a normal HTTPS link, verified website association, iOS Universal Links, Android App Links, app routing, installed-app handling, browser fallback, domain verification, and troubleshooting

A normal mobile application link often opens:

home screen

regardless of what the user intended to see.

A deep link can instead open:

https://example.com/products/42

      ↓

Product 42
inside the app

The same idea works for:

The challenge is making the link:

secure
predictable
verified
backward compatible
useful without the app

on both major mobile platforms.

Prefer a real HTTPS URL as the public contract

A good Universal Link or App Link is still a useful web URL. If the app is installed and the domain relationship is verified, the operating system can route the link into the app. If the app is unavailable, the same URL can continue to useful website content instead of becoming a dead link.

1. Understand what a deep link actually does

Deep linking is a general routing concept.

For example:

URL:
https://shop.example.com/products/42

route:
product

parameter:
42

The app translates that URL into:

navigate to ProductScreen
with product ID 42

Traditional custom URL schemes

Mobile apps can use schemes such as:

myshop://products/42

These remain useful for some app-to-app or internal flows.

But a custom scheme is not automatically tied to ownership of:

example.com

Verified HTTPS links solve a different problem

Universal Links and App Links establish:

website
      ↕
application

through platform-supported verification.

Apple terminology

Universal Links

Android terminology

App Links

Both are implementations of verified web-to-app linking, but their configuration formats differ.

2. Design stable HTTPS URLs before app routing

Do not begin deep linking with:

Which screen class
should we launch?

Begin with:

What is the stable public URL
for this resource?

Good link

https://example.com/orders/9814

Fragile link

https://example.com/open-screen?class=OrderActivity&index=4

Public links should represent:

product concepts

rather than internal implementation details.

Define a URL contract

/products/{productId}

/orders/{orderId}

/profile/{username}

/invite/{inviteToken}

Keep routes stable across app redesigns

Today:

/products/42
→ ProductScreen

Next year:

/products/42
→ ProductDetailFlow

The public URL does not need to change.

Use opaque IDs where exposing internal sequence numbers is undesirable

Avoid leaking sensitive information through predictable URL design.

Query parameters are useful but should remain controlled

https://example.com/search?q=laptop

is reasonable.

Do not create a generic router like:

?screen=anything
&action=anything
&payload=anything

that bypasses normal navigation and security boundaries.

3. Separate domain verification from app routing

Universal Links and App Links architecture (diagram)

Deep-linking architecture showing an HTTPS URL clicked from browser email or message, operating-system domain verification, Apple Universal Links through apple-app-site-association, Android App Links through assetlinks.json, installed-app routing, authentication checks, destination screen and browser fallback

Deep linking has two independent layers.

Layer 1: operating-system association

Does this app have permission
to claim links from this domain?

Layer 2: application routing

What should the app do
with this specific URL?

Association can succeed while routing fails.

For example:

link opens app

but app always shows home screen

That means:

platform verification:
probably working

internal router:
probably incomplete

The opposite can also happen.

Your router may perfectly parse:

/products/42

but the OS still opens the browser because the domain association is invalid.

4. Configure the iOS Associated Domain

iOS Universal Links setup flow (diagram)

iOS Universal Links setup flow showing HTTPS domain selection, Associated Domains capability, applinks domain entitlement, apple-app-site-association file creation, application identifier and URL component rules, web hosting verification, app installation, system association and internal route handling

Universal Links require an association in both directions.

app says:
I support example.com

website says:
this app may handle my links

Add Associated Domains capability

In the iOS target, configure:

Signing & Capabilities
      ↓
Associated Domains

Then add:

applinks:example.com

Subdomains are separate routing identities

If you use:

example.com
www.example.com
links.example.com

configure the domains intentionally rather than assuming one entry covers every deployment.

Do not put paths in the entitlement

Use:

applinks:example.com

not:

applinks:example.com/products/*

Path matching belongs in the website association configuration.

Use separate domains for separate environments where practical

links.example.com
staging-links.example.com

can reduce the risk of test applications claiming production links.

5. Create apple-app-site-association

The website needs:

apple-app-site-association

commonly hosted at:

https://example.com/.well-known/apple-app-site-association

There is no .json extension

Use:

apple-app-site-association

not:

apple-app-site-association.json

Minimal conceptual example

{
  "applinks": {
    "details": [
      {
        "appID": "ABCDE12345.com.example.app",
        "components": [
          {
            "/": "/products/*"
          },
          {
            "/": "/orders/*"
          }
        ]
      }
    ]
  }
}

Application identifier matters

The value combines the relevant Apple application identifier prefix with the app's bundle identifier.

A typo means:

domain association fails
even though JSON looks valid

Limit paths where appropriate

You may want:

/products/*
/orders/*
/invite/*

while ordinary informational pages remain browser-only.

Serve the file through HTTPS correctly

Check:

Remember that association information can be cached

When changing Universal Link configuration, do not assume every installed device immediately receives the new relationship.

Test installation and association behavior deliberately.

6. Route Universal Links inside the iOS app

After iOS recognizes a Universal Link, the application still needs to interpret it.

Normalize the URL

Example input:

https://example.com/products/42?campaign=summer

Router output:

route:
product

productId:
42

campaign:
summer

Use one central router

Avoid:

AppDelegate parses links one way

Scene code parses links another way

notification handler
uses third implementation

Prefer:

incoming URL
      ↓
DeepLinkParser
      ↓
DeepLinkRoute
      ↓
navigation coordinator

Example route model

enum DeepLinkRoute {
  product(id)
  order(id)
  profile(username)
  invitation(token)
}

Cold start and warm start differ

Test:

app terminated
+
link tapped

and:

app already running
+
link tapped

Both should reach the same destination.

Navigation may need to wait

On cold start:

link arrives
      ↓
app initializes
      ↓
session loads
      ↓
navigation becomes ready
      ↓
route link

Do not lose the pending deep link during startup.

7. Add Android App Link intent filters

Android App Links verification flow (diagram)

Android App Links verification flow showing Android manifest intent filter with VIEW BROWSABLE DEFAULT and autoVerify, HTTPS host, assetlinks.json retrieval, package name and SHA-256 signing fingerprint validation, verified domain state, URL click, app routing and browser fallback when verification fails

Android App Links begin with an intent filter.

Conceptual manifest example

<activity
    android:name=".MainActivity"
    android:exported="true">

    <intent-filter android:autoVerify="true">

        <action
            android:name="android.intent.action.VIEW" />

        <category
            android:name="android.intent.category.DEFAULT" />

        <category
            android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="http" />
        <data android:scheme="https" />

        <data android:host="example.com" />

    </intent-filter>

</activity>

autoVerify matters

android:autoVerify="true"

tells Android that the app intends to establish a verified association with the declared web domain.

VIEW, BROWSABLE, and DEFAULT

These identify the Activity as capable of receiving matching web-link intents.

Keep filters understandable

Multiple data elements in one filter can combine into URL patterns you did not initially intend.

If two URL families are conceptually independent, separate intent filters can make configuration easier to reason about.

Use a broad static host scope carefully

On modern Android versions, server-side Dynamic App Links can refine link behavior.

However, server rules cannot expand beyond the static scope permitted by the app manifest.

8. Publish assetlinks.json

Android validates the website relationship using:

https://example.com/.well-known/assetlinks.json

Conceptual example

[
  {
    "relation": [
      "delegate_permission/common.handle_all_urls"
    ],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.app",
      "sha256_cert_fingerprints": [
        "AA:BB:CC:..."
      ]
    }
  }
]

Package name must match the installed app

com.example.app

is not the same as:

com.example.app.debug

The signing certificate fingerprint is critical

Android verifies:

package name
+
SHA-256 signing certificate fingerprint

against the installed application.

Play App Signing changes which certificate matters

If Google Play signs the distributed application, the certificate on users' devices may differ from a local upload or development signing certificate.

Use the fingerprint corresponding to the actual distributed application identity.

Multiple fingerprints can be intentional

For controlled situations:

production signing certificate
+
another recognized certificate

can be represented when required by the deployment model.

Android 15+ Dynamic App Links

Newer Android versions can use optional server-side:

dynamic_app_link_components

in the Digital Asset Links relationship to refine matching by:

This can let link-routing scope evolve without shipping a new app version, provided the rules remain inside the scope allowed by the manifest.

9. Route Android App Links safely

Verification gets the URL into the app.

Navigation remains your responsibility.

Input

https://example.com/orders/9814

Parse

host:
example.com

path:
orders/9814

Map

OrderRoute(
  id = "9814"
)

Navigate

OrderDetailsScreen

Prefer typed routes

Avoid passing raw URLs deeply through the UI.

Prefer:

Uri
      ↓
parser
      ↓
validated route object
      ↓
navigation

Unknown paths should fail safely

/old-feature/99

should not crash the app.

Possible fallback:

open web page
or
show safe not-found screen

Do not assume one Activity forever

A navigation framework can route within a single Activity or coordinate several destinations.

The public URL should remain independent of internal Activity structure.

10. Design web fallback and authentication

A verified HTTPS link gives you a natural fallback.

App installed

https://example.com/products/42
      ↓
app
      ↓
Product 42

App not installed

https://example.com/products/42
      ↓
browser
      ↓
Product 42 web page

Do not force every browser visit into installation

The web page should remain useful where possible.

Authentication-aware routing

Suppose:

https://example.com/orders/9814

requires a logged-in user.

Correct flow:

link opened
      ↓
route parsed
      ↓
authentication required?
      ↓
login
      ↓
authorization check
      ↓
continue to order 9814

Preserve the intended destination during authentication.

Do not leak protected data before authentication

The URL itself should not contain:

private order contents
password
access token
long-lived credential

Invitation links need expiration

/invite/{opaque-token}

should normally be backed by:

11. Treat deep links as untrusted input

Domain verification proves:

this app is associated
with this website

It does not prove:

every URL parameter
contains safe trusted data

Validate IDs

/products/-999999999999999999

should not produce unsafe parser behavior.

Validate enumerations

Bad:

?action=execute_arbitrary_function

Better:

allow-list known actions

Do not perform destructive actions on open

Avoid:

/delete-account

causing immediate deletion merely because a URL was activated.

Use:

open confirmation screen
      ↓
authenticate if required
      ↓
user confirms
      ↓
server authorizes
      ↓
perform action

Authorization belongs on the backend

A user changing:

/orders/9814

to

/orders/9815

must not gain access to another user's resource.

Beware open redirects

Dangerous pattern:

/redirect?url=https://anything.example

can enable phishing or unexpected navigation if the destination is not constrained.

Be careful with JavaScript or WebView bridges

Do not let arbitrary deep-link data become:

JavaScript code
native method name
filesystem path

without strict validation.

12. Test verification and routing end to end

Deep links can appear correct in unit tests while real OS verification is broken.

Test the web URL first

Open:

https://example.com/products/42

in an ordinary browser.

Verify:

Test iOS association file

https://example.com/.well-known/apple-app-site-association

Verify the response contains the expected app identifier and URL rules.

Install a fresh iOS build

Association behavior can differ from an already-installed app whose domain metadata is cached.

Test iOS states

app terminated
app backgrounded
app foregrounded
user logged out
user logged in

Test Android assetlinks.json

https://example.com/.well-known/assetlinks.json

Verify:

Check Android verification state

Useful development commands include:

adb shell pm verify-app-links --re-verify PACKAGE_NAME

and:

adb shell pm get-app-links PACKAGE_NAME

Successful hosts should appear in the expected verified state.

Launch a URL through ADB

adb shell am start \
  -a android.intent.action.VIEW \
  -c android.intent.category.BROWSABLE \
  -d "https://example.com/products/42"

Test the production signing identity

A debug build succeeding does not prove the Play-distributed production build has the correct asset association.

Test every important subdomain

example.com
www.example.com
links.example.com

should each have intentional behavior.

13. Troubleshoot links that keep opening the browser

Problem: iOS link opens Safari

Check:

Associated Domains entitlement
AASA hostname
AASA app identifier
AASA path rules
HTTPS
redirects
fresh installation

Problem: Universal Link opens website while already browsing the same site

Browser context can affect whether navigation remains in the browser, particularly when the user is already browsing the same domain.

If the product needs an explicit web-to-app transition, design that journey and domain structure deliberately rather than assuming every same-domain tap will switch applications.

Problem: Android opens browser instead of app

Check:

autoVerify
VIEW action
DEFAULT category
BROWSABLE category
host
scheme
assetlinks.json
package name
certificate fingerprint
verification state

Problem: debug build works, production fails

Compare signing certificates.

debug certificate
!=
production app-signing certificate

Problem: one Android host breaks older-device verification

Multiple hosts deserve careful testing across the Android versions you support.

Do not add:

unused.example.com

to a verified filter without also providing the required association.

Problem: app opens, wrong screen appears

Platform verification is probably not the primary problem.

Inspect:

URL parsing
route mapping
startup timing
navigation state
authentication redirect

Problem: link works only when app is already running

The cold-start path is probably losing the route before navigation initialization completes.

Problem: link works only once

Check whether the router incorrectly treats:

handledDeepLink = true

as a lifetime state instead of tracking each incoming URL.

Problem: redirect breaks verification

Association files should be hosted exactly where the platform expects them. Do not depend on a marketing redirect chain to reach the association file.

14. Copy/paste deep-linking checklist

Deep linking checklist

Architecture
- Define why deep links are needed.
- Prefer HTTPS public URLs.
- Separate domain verification from app routing.
- Keep routing independent from internal screen class names.
- Define web fallback.
- Define authentication behavior.
- Define unknown-route behavior.
- Define supported app versions.

URL design
- Use stable URL paths.
- Model product concepts.
- Avoid exposing internal screen names.
- Avoid exposing implementation indexes.
- Use opaque IDs where appropriate.
- Keep routes backward compatible.
- Document query parameters.
- Validate every parameter.
- Avoid generic arbitrary-action parameters.

Example routes
- Define /products/{id}.
- Define /orders/{id}.
- Define /profile/{username}.
- Define /invite/{token}.
- Define fallback behavior.
- Define route ownership.

Web fallback
- Ensure HTTPS page exists.
- Keep page useful without app.
- Avoid installation-only dead ends.
- Support desktop where relevant.
- Handle unsupported app route.
- Handle old shared links.
- Avoid unnecessary redirect chains.

Custom URL schemes
- Use only where appropriate.
- Do not treat custom scheme as domain verification.
- Avoid privileged operations from scheme alone.
- Validate incoming values.
- Prefer Universal Links / App Links for public web URLs.

iOS
- Add Associated Domains capability.
- Add applinks domain.
- Verify production entitlement.
- Verify correct target.
- Separate staging domain where practical.
- Test multiple subdomains.
- Do not include paths in entitlement.

AASA
- Create apple-app-site-association.
- Do not add .json extension.
- Host at expected HTTPS location.
- Use valid TLS certificate.
- Avoid redirects.
- Make file publicly reachable.
- Use correct application identifier.
- Define applinks details.
- Define components / paths.
- Exclude paths intentionally when required.
- Validate JSON.
- Test production domain.

iOS app identifier
- Verify application identifier prefix.
- Verify bundle identifier.
- Verify production signing setup.
- Avoid copying another app's identifier.
- Document associated app IDs.

iOS path matching
- Match intended paths.
- Avoid claiming entire site without need.
- Test trailing slash.
- Test nested paths.
- Test query parameters.
- Test fragments where relevant.
- Test excluded routes.

iOS routing
- Parse URL centrally.
- Convert URL to typed route.
- Validate host.
- Validate path.
- Validate parameters.
- Preserve pending route during cold start.
- Handle warm-start route.
- Handle foreground route.
- Handle authentication.
- Handle unknown route.

iOS navigation
- Wait for navigation coordinator if necessary.
- Do not lose link during startup.
- Preserve post-login destination.
- Avoid duplicate navigation.
- Handle already-open destination.
- Handle stale resources.

iOS testing
- Test app terminated.
- Test app backgrounded.
- Test app foregrounded.
- Test logged-out user.
- Test logged-in user.
- Test fresh install.
- Test reinstall after AASA change.
- Test real device.
- Test production domain.
- Test same-domain browser behavior.

Android
- Add VIEW intent action.
- Add DEFAULT category.
- Add BROWSABLE category.
- Add http scheme.
- Add https scheme.
- Add expected host.
- Add android:autoVerify=true.
- Use exported activity where required.
- Test manifest merging.
- Keep intent filters understandable.

Android hosts
- Verify every declared host.
- Verify every relevant subdomain.
- Avoid accidental host combinations.
- Separate filters when URL families differ.
- Test wildcard behavior deliberately.
- Do not declare unused domains.

assetlinks.json
- Create valid JSON.
- Host at /.well-known/assetlinks.json.
- Serve through HTTPS.
- Use correct relation.
- Use android_app namespace.
- Use exact package name.
- Use SHA-256 signing fingerprint.
- Verify production signing certificate.
- Avoid redirects.
- Test file publicly.

Play App Signing
- Check whether Play App Signing is enabled.
- Use Play app-signing certificate fingerprint for distributed app.
- Do not assume local upload certificate is the installed certificate.
- Record signing identities.
- Plan certificate changes.
- Test production release.

Android verification
- Install app.
- Allow verification to complete.
- Re-run verification when testing.
- Check pm get-app-links.
- Confirm host reports verified.
- Investigate all non-verified states.
- Test real HTTPS link.
- Test multiple Android versions.

ADB testing
- Reset link state when required.
- Trigger verify-app-links.
- Query get-app-links.
- Launch ACTION_VIEW with test URL.
- Verify destination Activity.
- Capture logs.
- Test production package.

Android 15+
- Consider Dynamic App Links where useful.
- Keep broad static scope only when appropriate.
- Keep dynamic rules inside manifest scope.
- Version server-side rules.
- Test devices without dynamic-rule support.
- Keep older Android path behavior compatible.
- Test query and path matching.

Internal router
- Parse host.
- Parse path.
- Parse query.
- Parse fragment only when intentionally used.
- Normalize URL.
- Create typed route.
- Reject malformed input.
- Reject unsupported host.
- Reject unsupported action.
- Route through normal navigation.

Authentication
- Determine whether route requires login.
- Preserve original route.
- Authenticate.
- Return to requested destination.
- Re-check authorization after authentication.
- Handle expired invitation.
- Handle deleted resource.
- Handle revoked account.

Authorization
- Enforce server-side ownership.
- Never trust route ID as authorization.
- Prevent IDOR-style access.
- Validate roles.
- Validate resource access.
- Avoid privileged actions on link open.

Security
- Treat URL as untrusted input.
- Validate numeric IDs.
- Validate UUIDs.
- Validate token format.
- Validate enumerations.
- Limit lengths.
- Decode URL safely.
- Avoid arbitrary command execution.
- Avoid arbitrary filesystem paths.
- Avoid arbitrary WebView JavaScript.
- Avoid open redirects.

Sensitive actions
- Never delete content automatically from a deep link.
- Never transfer money automatically from a deep link.
- Never change email automatically from a deep link.
- Require confirmation.
- Require authentication.
- Re-check authorization.
- Protect CSRF-like workflows where applicable.

Invitation links
- Use opaque token.
- Set expiration.
- Limit use where appropriate.
- Validate server-side.
- Avoid leaking private data in URL.
- Handle already-used invitation.
- Handle revoked invitation.

Password reset
- Use high-entropy token.
- Set expiration.
- Validate server-side.
- Make token limited-purpose.
- Avoid logging full token.
- Avoid analytics capture.
- Clear token from app state when finished.

Analytics
- Track route type.
- Track link source when appropriate.
- Avoid logging secret query parameters.
- Avoid storing reset tokens.
- Avoid storing invitation secrets.
- Sanitize analytics URLs.
- Record routing failure category.

Marketing parameters
- Allow known UTM-style metadata.
- Keep campaign data separate from navigation authorization.
- Do not let marketing parameters control privileged behavior.
- Strip unnecessary parameters before internal routing.

Redirects
- Minimize redirects.
- Avoid redirecting association files.
- Test www to apex behavior.
- Test HTTP to HTTPS behavior.
- Ensure final canonical URL remains supported.
- Do not build redirect loops.

Subdomains
- Document each associated subdomain.
- Host association file where required.
- Configure iOS entitlement.
- Configure Android host.
- Verify certificates.
- Avoid wildcard assumptions.
- Test each host independently.

Staging
- Use staging app identifier.
- Use staging Android package name.
- Use staging signing fingerprint.
- Use staging AASA.
- Use staging assetlinks.json.
- Avoid staging app claiming production URLs unless intentional.
- Keep analytics separate.

Multiple apps
- Decide which app owns which paths.
- Avoid overlapping routing ambiguity.
- Keep association files explicit.
- Test installed combinations.
- Document app priority assumptions.
- Avoid two activities claiming identical Android links without reason.

Multiple Android variants
- Handle debug package separately.
- Handle staging package separately.
- Include fingerprints intentionally.
- Avoid publishing debug association to production unless required.
- Test Play-distributed package.

Backward compatibility
- Keep old URLs working.
- Redirect deprecated paths safely.
- Map legacy routes.
- Maintain server fallback.
- Avoid removing shared campaign links immediately.
- Monitor route usage before retirement.

Cold start
- Capture incoming URL.
- Initialize session.
- Initialize navigation.
- Route when ready.
- Avoid losing URL.
- Avoid routing twice.
- Test slow initialization.

Warm start
- Receive new link.
- Avoid resetting entire navigation unnecessarily.
- Route relative to current state.
- Handle duplicate tap.
- Handle same destination.
- Preserve unsaved work according to product policy.

Unknown link
- Do not crash.
- Show safe fallback.
- Open web page where appropriate.
- Log route mismatch.
- Avoid infinite app-to-browser loop.

WebView
- Decide whether links remain in WebView.
- Decide whether verified domain should open native route.
- Validate navigation.
- Prevent arbitrary scheme execution.
- Avoid exposing unsafe JavaScript bridges.

Notifications
- Reuse same deep-link router where practical.
- Avoid separate routing grammar.
- Validate notification URL.
- Preserve security checks.
- Test cold and warm start.

QR codes
- Use HTTPS link where practical.
- Reuse existing routing contract.
- Validate scanned data.
- Avoid custom arbitrary actions.
- Provide web fallback.

Email links
- Use stable HTTPS URL.
- Avoid exposing private content in query string.
- Test mail clients.
- Test app installed.
- Test app not installed.
- Test expired links.

Testing matrix
- iPhone app installed.
- iPhone app not installed.
- Android app installed.
- Android app not installed.
- App terminated.
- App backgrounded.
- App foregrounded.
- Logged in.
- Logged out.
- Valid route.
- Unknown route.
- Deleted resource.
- Expired token.
- Invalid parameter.
- Staging environment.
- Production environment.

Monitoring
- Track link opens.
- Track app-routing success.
- Track browser fallback.
- Track invalid routes.
- Track authentication interruptions.
- Track destination load failure.
- Track verification incidents after releases.

Release process
- Verify AASA before release.
- Verify assetlinks.json before release.
- Verify production iOS entitlement.
- Verify Android production fingerprint.
- Verify package name.
- Test store-signed Android build.
- Test App Store / TestFlight iOS build where appropriate.
- Test representative links.

Troubleshooting iOS
- Check Associated Domains.
- Check app identifier.
- Check AASA filename.
- Check HTTPS.
- Check redirect.
- Check hostname.
- Check component rules.
- Reinstall app.
- Test real device.
- Check browser context.

Troubleshooting Android
- Check autoVerify.
- Check VIEW.
- Check DEFAULT.
- Check BROWSABLE.
- Check schemes.
- Check host.
- Check assetlinks.json.
- Check package name.
- Check fingerprint.
- Check domain verification state.
- Check user link preferences.

Final review
- Is the public link a valid HTTPS URL?
- Does it provide a useful browser fallback?
- Is the URL contract stable?
- Is the iOS Associated Domain configured?
- Is AASA correctly hosted?
- Is the iOS app identifier correct?
- Are iOS paths correct?
- Does iOS handle cold and warm starts?
- Is Android autoVerify enabled?
- Is assetlinks.json correctly hosted?
- Is the package name correct?
- Is the production signing fingerprint correct?
- Does Android report the domain as verified?
- Are Android 15 dynamic rules compatible with older versions?
- Are all parameters validated?
- Does authentication preserve the destination?
- Does the backend enforce authorization?
- Can malformed links fail safely?
- Do sensitive actions require confirmation?
- Are secret tokens excluded from analytics and logs?
- Have app-installed and app-not-installed flows both been tested?
- Can old shared links remain useful after future app redesigns?

15. FAQ

What is a deep link?

A deep link is a URL or URI that routes a user to a specific location or action inside an application instead of only opening the application's default home screen.

What is the difference between Universal Links and App Links?

Universal Links are Apple's verified HTTPS linking mechanism. App Links are Android's equivalent verified web-link mechanism. Both require an association between the website and application, but their configuration formats and platform APIs differ.

Where does apple-app-site-association go?

A standard deployment serves the file over HTTPS from the domain's well-known location using the filename apple-app-site-association without a .json extension. The app needs a matching Associated Domains configuration.

Where does Android assetlinks.json go?

It is served from the domain's HTTPS /.well-known/assetlinks.json location and identifies the Android package and signing certificate authorized to handle the links.

Why does my Android App Link work in debug but fail from Google Play?

The production app can be signed with a different certificate from your local debug or upload build. Verify that assetlinks.json contains the SHA-256 fingerprint of the certificate that actually signs the installed production application.

Why does the app open but show the wrong screen?

If the operating system already launches the app, domain verification may be working correctly. Inspect URL parsing, route mapping, initialization, authentication redirects, and navigation state inside the app.

Should a deep link perform a sensitive action immediately?

Usually not. Treat deep-link parameters as untrusted input, authenticate the user when required, enforce authorization on the server, and require explicit confirmation before destructive or high-impact operations.

Key terms (quick glossary)

Deep link
A URL or URI that maps to a specific destination or action inside a mobile application.
Universal Link
Apple's verified HTTPS linking mechanism that associates website URLs with an application.
App Link
Android's verified HTTP or HTTPS linking mechanism that associates website URLs with an installed Android application.
Associated Domains
Apple application capability and entitlement used for services including Universal Links.
apple-app-site-association
The website-hosted association file that identifies Apple applications authorized to handle specified Universal Link URLs for a domain.
AASA
Common abbreviation for the apple-app-site-association file.
Digital Asset Links
Android's mechanism for declaring verified relationships between web origins and applications.
assetlinks.json
The website-hosted Digital Asset Links statement file used for Android App Link verification.
autoVerify
Android manifest intent-filter setting indicating that the operating system should attempt to verify the declared web domains.
Intent filter
Android manifest declaration describing which external intents and URL patterns an Activity can handle.
Signing certificate fingerprint
A cryptographic fingerprint identifying the certificate used to sign an Android application for Digital Asset Links verification.
Dynamic App Links
Android 15-and-later functionality that can refine supported App Link matching rules through server-side Digital Asset Links configuration.
Deep-link router
Application component that parses incoming URLs, validates parameters, maps them to typed destinations, and triggers navigation.
Cold start
Launching the application from a state where its process or navigation hierarchy is not already running.
Warm start
Handling a new link while the application process or user interface is already active.
Web fallback
The website experience shown when a verified app link is not opened in the native application.
Custom URL scheme
An application-defined URI scheme such as myapp:// that can route into an app but does not by itself provide the same website-domain verification as Universal Links or App Links.

Found this useful? Share this guide: