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.
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.
On this page
- 01Which SHA-256 belongs in assetlinks.json when Play App Signing is on?
- 02How do I copy the correct app signing SHA-256 from Play Console?
- 03What does a correct assetlinks.json look like?
- 04Where does a fingerprint mismatch show up?
- 05How do I verify the fix, and in what order?
- 06What breaks an otherwise correct assetlinks.json?
- 07Does a broken assetlinks.json affect Google Play review?
- 08Frequently Asked Questions
- 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.
Which SHA-256 belongs in assetlinks.json when Play App Signing is on?
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.
| Key | Who holds it | What it signs | In assetlinks.json? |
|---|---|---|---|
| Upload key | You, in a local keystore | The 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 key | Google Play | The final APKs delivered to users' devices from your bundle. | Yes. This is the one that fixes the Play install. |
| Quantum-ready hybrid keys | Google Play | Installs 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.
What does a correct assetlinks.json look like?
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)
| Requirement | What it looks like in practice | Typical failure |
|---|---|---|
| Exact path | https://example.com/.well-known/assetlinks.json, including the dot in .well-known | A copy in a subdirectory such as /app/.well-known/ is never fetched. |
| Direct HTTP 200 | The file body, immediately | A login page or an HTML error page answering 200 is not a valid statement list. |
| No redirects | No 301 or 302 hops | An http to https redirect, or a bare domain redirecting to www, is not followed. |
| Content-Type: application/json | Set by the server, not the CDN default of text/html | - |
| A file on every origin you open | example.com and www.example.com are different origins | A 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 see | What is happening | Where to confirm it |
|---|---|---|
| A web link to your domain opens the browser, or opens an app-picker dialog | The association never verified, so the link is treated as an ordinary web link | adb shell pm get-app-links on the device |
| A Trusted Web Activity shows the browser bar only in the Play copy | The Play install carries the app signing key certificate, which your statement does not list | Statement List Generator, with the app signing key fingerprint |
| The Domains table on Play Console's Deep links page says Not OK | Google's own check of your hosted file failed | Hover the status column for the specific failed checks |
The Statement List Tester returns no statement, or "linked": false | The package and certificate combination is not in your published list | assetlinks.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.
- 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.
- 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. - 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.
- 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.
What breaks an otherwise correct assetlinks.json?
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 version | Which key signs what Google delivers | Fingerprint to keep listed |
|---|---|---|
| 17 and higher | The upgraded quantum-ready hybrid keys, verified with APK signature scheme v3.2 | The new classical key and the post-quantum key |
| 13 to 16 | The latest classical key, enforced by the platform | The latest classical key |
| 12 and lower | The legacy key, still recognised after an upgrade | The 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".
Does a broken assetlinks.json affect Google Play review?
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.
- Configure website associations and dynamic rules (statement fields, multiple fingerprints, the Play App Signing fingerprint warning, the Play Console JSON snippet)
- Use Play App Signing (upload key versus app signing key, quantum-ready hybrid signing, key upgrade enforcement, the assetlinks.json tip)
- Creating a Statement List (HTTP 200, no redirects, Content-Type requirements)
- Verify App Links (auto verification, adb commands, domain verification states, update latency)
- Verify and maintain deep links (Deep links page, Domains table, additive JSON generator, patches)
- Share app bundles and APKs internally (internal app sharing key re-signing)
- Multi-origin Trusted Web Activities (Custom Tab UI after leaving a validated origin)
- App Links developer tools (App Links Assistant, Play Console Deep links dashboard)
- assetlinks.check API and the Statement List Generator and Tester

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.
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 testRelated articles
How Long Google Play Review Actually Takes in 2026
Google publishes a duration for only a few of its review clocks. Here is what it says for each one, plus the timing claims that have no Google source.
Target API Level Error in Play Console: How to Fix It (2026)
Google Play blocks submissions below API 36 from 31 August 2026. Find the target API level issue on Policy status, verify your bundle, use the extension.
Google Play Store Listing Review Checklist: Pass First Time
Every store listing field Google checks at review, the rejection each wrong field triggers, and the exact Play Console fix to pass review once.