One App Links host fails verification

One unverified host can break every App Link on Android 11 and lower. How per-host verification differs on Android 12 and how to find and fix the failing host.

SymptomOn Android 11 and lower no host opens the app, or on Android 12 and later one subdomain opens the browser.
Likely causeOne host in the manifest has no valid assetlinks.json, such as a staging, VPN-only or extra subdomain.
FixPublish assetlinks.json on every host in the manifest, or remove hosts that cannot serve it from release builds.

Short answer: Android verifies each host in your intent filters. On Android 11 and lower, Google says "verification fails if even one host cannot be verified", so one staging or VPN-only host breaks App Links for all of them. On Android 12 and later, hosts verify independently, so only the failing host opens the browser. Publish the file on every host or remove the extra host.

What is happening

Per-host verification: Android's check of https://<host>/.well-known/assetlinks.json for every host declared in App Link intent filters.

The rules changed with Android 12:

  • Android 11 and lower: the app becomes the default handler "only if it finds a matching Digital Asset Links file for all hosts in the manifest".
  • Android 12 and later: the system "attempts to verify each one. If any host is verified, the app becomes the default handler for links from that verified host."

So the same manifest can break everything on older devices and only one subdomain on newer ones.

Why does this happen?

Manifests collect hosts over time: staging domains, marketing subdomains, regional sites. Any host without a reachable, valid file fails.

  • Staging or VPN-only hosts: Google's servers cannot reach them.
  • Subdomains without a file: Google treats non-wildcard subdomains as "unique, separate hosts".
  • Wildcard hosts: for *.example.com, the file must be at the root hostname, example.com.
  • Redirects on one host: for example www serves the file but the apex redirects.

How to confirm it

  1. List every android:host value in your intent filters.
  2. For each, run curl -i https://<host>/.well-known/assetlinks.json and check for a 200, application/json and no redirect.
  3. On an Android 12 or later device, run adb shell pm get-app-links com.example.app. Each domain is listed with its own state; only verified is a pass.
  4. Check each host with https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://<host>&relation=delegate_permission/common.handle_all_urls.

How to fix it

  1. Publish assetlinks.json on every host the manifest declares.
  2. Remove staging, internal and VPN-only hosts from release builds, so shipped apps only declare hosts that serve the file.
  3. For wildcard hosts, serve the file at the root hostname.
  4. Remove redirects on /.well-known/assetlinks.json for every host.
  5. Reinstall and re-verify:
  • adb shell pm set-app-links --package com.example.app 0 all
  • adb shell pm verify-app-links --re-verify com.example.app
  • wait a few minutes, then adb shell pm get-app-links com.example.app
<!-- Release manifest: only hosts that serve assetlinks.json -->
<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" />
    <data android:host="www.example.com" />
</intent-filter>

Check each host's file with curl -I https://<host>/.well-known/assetlinks.json and Google's Statement List tester, then read the per-host state with adb shell pm get-app-links com.example.app. For delivery problems on one host, see assetlinks redirect.

FAQ

Common questions

Why do App Links work on a newer phone but not an older one?

Newer Android versions verify each host separately, while Android 11 and lower need every host to verify. A single failing host breaks all links only on the older device.

Does a file on example.com cover www.example.com?

No. Google treats non-wildcard subdomains as separate hosts, so `www.example.com` needs its own file. Only a wildcard host uses the file at the root hostname.

How do I see which host failed?

On Android 12 and later, `adb shell pm get-app-links` lists every domain with its state. Any domain not marked `verified` is the one to fix.

Should staging hosts be in the release manifest?

No. Google's guidance is to publish the file on every host or remove the host, and a staging or VPN-only host cannot be reached for verification.

What are UTM parameters?

UTM parameters are tags added to the end of a link, such as utm_source and utm_campaign, that tell analytics tools where a visit came from. They let you see which campaign, channel and ad brought each visitor.

Which UTM parameters are required?

Use utm_source, utm_medium and utm_campaign on every campaign link. utm_term and utm_content are optional and help you compare keywords or ad variants.