Play Console & Shipping17 min read

assetlinks.json SHA-256: Fix Play App Signing Mismatches

Play App Signing re-signs your app, so the fingerprint your keytool prints is not the one Android checks. Here is the SHA-256 to publish and how to verify it.

Ahmed Akash
Ahmed Akash

Need testers who stay active for all 14 days?

TesterBee matches you with 12 real Android testers in under 24 hours. Refund if Google rejects you over tester engagement.

Get 12 testers
On this page
  1. 01Which SHA-256 belongs in assetlinks.json when Play App Signing is on?
  2. 02How do I copy the correct app signing SHA-256 from Play Console?
  3. 03What does a correct assetlinks.json look like?
  4. 04Where does a fingerprint mismatch show up?
  5. 05How do I verify the fix, and in what order?
  6. 06What breaks an otherwise correct assetlinks.json?
  7. 07Does a broken assetlinks.json affect Google Play review?
  8. 08Frequently Asked Questions
  9. 09Sources

assetlinks.json SHA-256: Fix Play App Signing Mismatches

If your verified app links open in the browser instead of your app, or your Trusted Web Activity shows a browser bar only on the copy installed from Google Play, the certificate in your assetlinks.json is the first thing to check. When Play App Signing is on, Google re-signs the APKs it delivers with an app signing key it holds, so the fingerprint your keytool prints on your own machine is usually not the fingerprint on users' devices. Android matches the certificate of the installed app against the sha256_cert_fingerprints array in https://your-domain/.well-known/assetlinks.json, and a mismatch fails verification silently: no error email, no Play Console alert, just a link that opens Chrome. This guide covers which SHA-256 belongs in the file, where to copy it from in Play Console, a working example you can adapt, the order to verify each layer in, and the cases that break an otherwise correct file.

The SHA-256 of the app signing key certificate, the one Play Console shows on your Play app signing page. Not the upload key, and not the fingerprint that keytool prints from the keystore on your laptop. The installed app on a user's device was signed by Google with the app signing key, so that is the certificate the device checks your statement against.

Google states the trap directly in its App Links documentation: "If you're using Play App Signing for your app, then the certificate fingerprint produced by running keytool locally will usually not match the one on users' devices." (source) The same page tells you where the correct value lives: if Play App Signing is enabled, "you'll also find the correct Digital Asset Links JSON snippet for your app on the same page" in Play Console.

The reason is that Play App Signing splits your identity into two keys, and only one of them signs what users install.

KeyWho holds itWhat it signsIn assetlinks.json?
Upload keyYou, in a local keystoreThe app bundle you upload. Google uses it to verify your identity as the uploader.Only if you also want builds you install yourself to verify.
App signing keyGoogle PlayThe final APKs delivered to users' devices from your bundle.Yes. This is the one that fixes the Play install.
Quantum-ready hybrid keysGoogle PlayInstalls on Android 17 and higher, using a new classical key plus a post-quantum ML-DSA-65 key, alongside the classical key used on older devices.Google's current instruction is to add those fingerprints too. See below.

Google's Play App Signing help describes the app signing key in exactly the terms that matter here: "Google uses this key to sign the final APKs delivered to users' devices." (source) Since the certificate on the device is the app signing key certificate, an assetlinks.json built from your upload keystore describes an app that nobody installs from Play. Nothing errors. Android simply records the domain as unverified and keeps opening links in the browser.

If signing-key concepts are new to you, our guide on publishing a Flutter app on Google Play walks through the same upload-key and app-signing-key split from the release side.

How do I copy the correct app signing SHA-256 from Play Console?

Open your app in Play Console and go to the Play app signing page. The current Play App Signing help names the route as Protected with Play, then Play Store protection, then Manage Play app signing; Google's App Links documentation points at the same page under its earlier name, Release, then Setup, then App signing. Scroll to the App signing key section and copy the SHA-256 certificate fingerprint. It is 32 pairs of hexadecimal characters separated by colons. Copy the whole value, and do not confuse it with the SHA-1 line printed next to it or with the Upload key certificate section above.

Apps enrolled in quantum-ready hybrid signing need more than one fingerprint

Google's current documentation describes new Play Console apps as enrolled by default in quantum-ready hybrid signing, which uses three keys: a Google-generated classical key for devices running Android 16 and lower, plus a new classical key and a post-quantum ML-DSA-65 key that devices running Android 17 and higher verify with. Google's own instruction on the Play app signing page is: "Update your assetlinks.json file with these fingerprints if you use Android App Links." (source) In practice that means listing every app signing key fingerprint Play shows for your app, not just one.

Play Console can generate the file for you

Two official generators exist, and both already know your app's certificates.

  • The Play app signing page shows the correct Digital Asset Links JSON snippet for the app, as the App Links documentation notes.
  • The Deep links page (Grow users, then Deep links) has a Domains table listing every domain declared in your manifest with an OK or Not OK status. Hovering the status shows which checks failed, and the overlay includes a JSON generator. Google describes it as additive: "It's completely safe to take all the contents and paste as they are (or download the file) - it won't break compatibility with other apps you might own. We highlight the proposed changes in green." (source)

Using the generator is the fastest way to rule out a transcription error, which is the most common version of this bug: a fingerprint pasted from a blog post, truncated at the end, or missing its colons.

A statement list is a JSON array. Each statement pairs a permission with one Android app, identified by its package name and an array of certificate fingerprints. This is Google's documented example format, extended with a second fingerprint so builds you install yourself also verify:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.app",
      "sha256_cert_fingerprints": [
        "AA:BB:CC:...32 pairs from Play Console...:FF",
        "11:22:33:...your upload key, optional...:44"
      ]
    }
  }
]

Replace the package name with your exact application ID, and each placeholder with a full colon-separated SHA-256. Delete the second entry if only Play installs need to verify. Android's website association guide confirms the array is designed for this: "This field supports multiple fingerprints, which can be used to support different versions of your app, such as debug and production builds." (source) A different application ID is a different app and needs its own statement in the same array.

How the file must be served

The location and the HTTP behaviour are specified, not conventional. Google's Digital Asset Links documentation states: "Any response from the server besides HTTP 200 is treated as an error, and will result in an empty statement list." It also requires that the file "must be served as Content-Type: application/json in the HTTP headers, and it cannot be a redirect (that is, 301 or 302 response codes are not followed)." (source)

RequirementWhat it looks like in practiceTypical failure
Exact pathhttps://example.com/.well-known/assetlinks.json, including the dot in .well-knownA copy in a subdirectory such as /app/.well-known/ is never fetched.
Direct HTTP 200The file body, immediatelyA login page or an HTML error page answering 200 is not a valid statement list.
No redirectsNo 301 or 302 hopsAn http to https redirect, or a bare domain redirecting to www, is not followed.
Content-Type: application/jsonSet by the server, not the CDN default of text/html-
A file on every origin you openexample.com and www.example.com are different originsA link from your app to an origin you do not host a file on fails verification there.

Android queries this file automatically at install time when an intent filter carries android:autoVerify="true": "For each unique hostname found in the intent filters, Android queries the corresponding websites for the Digital Asset Links file at https://hostname/.well-known/assetlinks.json." (source) Nothing tells you when that query returns empty.

Where does a fingerprint mismatch show up?

Four places, and they fail independently. Checking them in order tells you whether the problem is the file, Google's view of the file, or the device.

What you seeWhat is happeningWhere to confirm it
A web link to your domain opens the browser, or opens an app-picker dialogThe association never verified, so the link is treated as an ordinary web linkadb shell pm get-app-links on the device
A Trusted Web Activity shows the browser bar only in the Play copyThe Play install carries the app signing key certificate, which your statement does not listStatement List Generator, with the app signing key fingerprint
The Domains table on Play Console's Deep links page says Not OKGoogle's own check of your hosted file failedHover the status column for the specific failed checks
The Statement List Tester returns no statement, or "linked": falseThe package and certificate combination is not in your published listassetlinks.check with the exact package and fingerprint

Two of these deserve a note. First, a Trusted Web Activity that works everywhere except after a login or a redirect is not a fingerprint problem: Chrome documents that "When a user navigates off the validated origin, Custom Tab UI is shown." (source) Your association is intact; you simply left the origin it covers. Second, the browser bar appearing only on the Play build is the clearest fingerprint symptom there is, because the same website and the same code verify from a locally installed build.

How do I verify the fix, and in what order?

Work outwards from the file. Each step answers one question, and passing an earlier step never proves a later one.

  1. Is the live file reachable? Request it directly and read the status line, the redirect chain and the content type.
curl -sI https://example.com/.well-known/assetlinks.json

You want a single HTTP 200 with Content-Type: application/json and no Location header. If your server or CDN caches aggressively, purge it before you conclude anything.

  1. Does Google see the statement? Run your domain, package name and app signing key fingerprint through Google's Statement List Generator and Tester. For an exact answer, call the assetlinks.check method and look for "linked": true. A false here means the file or the fingerprint is wrong, and no device will verify until it changes.
  2. What does Play Console say? Open Grow users, then Deep links, and read the Domains table. A Not OK status with a hover explanation usually names the failing check: unreachable file, wrong content type, missing statement, or a package and fingerprint pair Play cannot match.
  3. What does a device say? Install the app from a Play testing track, not from Android Studio, then force a fresh verification and read the result.
adb shell pm set-app-links --package com.example.app 0 all
adb shell pm verify-app-links --re-verify com.example.app
adb shell pm get-app-links com.example.app

Google documents these commands for Android 12 and higher. The output prints a verification state per domain: verified means the domain is verified for your app, while none means "the verification agent might not have completed the verification process yet", and legacy_failure means the domain was rejected without a specific reason recorded. Any value of 1024 or greater is a custom error code from that device's verifier, not a diagnosis of a wrong SHA-256. (source)

Then tap a real link. The device test is the only layer that reproduces what a user does, and it is where a fingerprint problem and a navigation problem finally separate.

Test the build Play delivers, not the build you installed

This is the step most often skipped, and it is the one that decides whether your fix works. In the closed-testing campaigns we run, across more than 1,500 campaigns on Samsung, Pixel, Xiaomi and OnePlus devices covering Android 11 to 15 in over 80 countries, every tester installs the build Google Play delivers, which is signed with the app signing key. A build you side-load from Android Studio is signed with your debug or upload key, so it can verify against a statement that lists your local certificate and tell you nothing about the Play install. Google points at the same idea when it suggests testing "exactly what Google Play will deliver to users" through internal app sharing, though see the caveat below before you rely on that route for this specific check. If you cannot get a testing-track build onto a device, the Play pre-launch report runs on Google's own devices against the uploaded build.

A file that verified last month can stop verifying without anyone touching it. These are the five cases worth checking before you rewrite anything.

1. Internal app sharing links

Uploads made through internal app sharing are not signed with your keys at all: Google states that "They're automatically re-signed with an Internal App Sharing key, which is automatically created for your app by Google." (source) That key is not in your statement, so a build installed from an internal sharing link will fail verification by design. Test on a testing track instead.

2. A key upgrade or rotation

Google allows an annual app signing key upgrade, and the new key does not replace the old one everywhere at once. Its published enforcement split is worth reading before you touch your fingerprint array: Android 17 and higher strictly enforce the upgraded key, Android 13 to 16 strictly enforce the latest classical key, and on Android 12 and lower "the Android platform itself does not enforce the upgraded key and still recognizes the latest classical key", with Play Protect providing additional validation. (source) Users on older versions keep running builds signed with the previous key, so an old fingerprint must stay in the array while those installs are still live. Removing it verifies your new installs and silently breaks the old ones.

Android versionWhich key signs what Google deliversFingerprint to keep listed
17 and higherThe upgraded quantum-ready hybrid keys, verified with APK signature scheme v3.2The new classical key and the post-quantum key
13 to 16The latest classical key, enforced by the platformThe latest classical key
12 and lowerThe legacy key, still recognised after an upgradeThe pre-upgrade key

3. Distribution outside Google Play

If you hand your app signing key to another store, that store signs with the same certificate and one fingerprint covers both channels. If instead you download a signed universal APK from Play Console (Test and release, then Latest releases and bundles, then the Downloads tab) to distribute elsewhere, that APK is still signed with the app signing key, so your existing statement already covers it. Only builds signed with a key that is not listed in the array need a new fingerprint.

4. Debug builds and other local variants

Adding your debug certificate to the array makes debug builds verify too, which is useful during development and harmless in production, since possession of a public fingerprint grants nothing. Keep in mind that the array authorises a package name and certificate pair, so a second application ID needs its own statement.

5. Latency after a change

A corrected file does not reach every device at the same moment. Google documents that on Android 15 and higher the system re-verifies domains in the background and "Changes can take up to seven days to propagate to all end-user devices due to the system's caching and scheduled re-verification", while on Android 14 and lower updates "are typically only picked up when the app is installed or updated". (source) During testing, uninstalling and reinstalling forces a re-check, though Google warns that server-side caches can still delay delivery and that the delay "typically resolves automatically if you try again after a few hours".

Not as a documented review criterion, and it is worth being precise about that. App Links verification happens on the device, after install: the OS fetches your statement and decides how to route links. Play Console hosts the Deep links page under Grow users, which Google presents as an engagement and diagnostics surface with a Domains table, status checks and patches you can publish "without releasing a new app version". (source) We found no statement in Google's published policy or review documentation listing a failing Digital Asset Links statement as a rejection reason, and we would not present one as a requirement without it.

What it does cost you is real but user-facing: links that land in the browser, a Trusted Web Activity that shows a URL bar, and campaign traffic that never reaches the in-app content you deep linked. If you are reading this after a rejection email rather than after a broken link, the notice is about something else, and our Broken Functionality checks cover what Google actually inspects when it says an app did not work.

Frequently Asked Questions

Which SHA-256 goes in assetlinks.json when Play App Signing is enabled?

The app signing key certificate SHA-256 shown on your Play app signing page in Play Console, because that certificate signs the builds Play delivers to devices. Add your upload key fingerprint as well only if you want builds you install yourself to verify, and add every additional app signing key fingerprint Play displays if your app uses quantum-ready hybrid signing.

Can I list both the app signing and the upload certificate?

Yes. The sha256_cert_fingerprints field is an array, and Google documents it as supporting multiple fingerprints for different versions of the same app. Listing both is the usual setup for developers who test their own release builds locally and distribute through Play.

Why does my app link work from Android Studio but not from Google Play?

Because the two builds carry different certificates. A build you run from Android Studio is signed with your local debug or upload key, while the build Play installs is signed with the app signing key. If your statement lists only the local certificate, the Play install fails verification and falls back to the browser, which is exactly the mismatch this article fixes.

How long after fixing assetlinks.json does verification take?

On Android 15 and higher, Google documents up to seven days for changes to propagate to all devices because of caching and scheduled background re-verification. On Android 14 and lower, updates are typically picked up when the app is installed or updated, so a fresh install is usually enough. Server-side caches on your own CDN can add a few more hours.

Do example.com and www.example.com need separate files?

Yes. They are different origins, and Android fetches the file from the hostname in your intent filter. Publish the same file on each origin your app opens, and remember that a redirect from one to the other is not followed.

Will a broken assetlinks.json get my app rejected in Play review?

We found no Google documentation naming a failing Digital Asset Links statement as a review rejection reason. The damage is user-facing: links that open in the browser instead of the app. If you are troubleshooting a rejection notice, start from the policy side rather than the certificate side.

Sources

All Google and Chrome documentation below was read on 27 October 2026.

Ahmed Akash
Ahmed Akash·Senior Full Stack Developer & Founder of TesterBee

Senior full stack developer specializing in Laravel, React, and Firebase. Helped 1,300+ Android developers pass Google Play Closed Testing and publish apps. Expert in Play Console, production access, and Android app publishing.

October 27, 2026 · 17 min readLinkedInGitHub

Get 12 testers into your closed test in 24 hours

Matched Android testers who open your app every day of the 14-day window, so your production access review has real engagement to look at.

Start your closed test