Chrome Tells Every Server Your Phone Runs Android 10
Since 2023, Chrome on Android sends the same user agent from every phone: Android 10, model K. What that did to deferred deep link matching, why we didn’t fix it with Critical-CH, and how Appy link domains now get the real version and model back. Plus the iOS 26 version of the same problem.
Every Android phone is a “K” now
Take a Pixel on the latest Android and a Galaxy that is four years old. Open the same page in Chrome on both. Apart from the Chrome version, the User-Agent header they send is identical: Android 10, device model K.
That isn’t a bug. It is user-agent reduction, a Chrome privacy change that froze the parts of the string that helped fingerprint a device. On Android it came with Chrome 110. According to the Chromium project’s timeline, the rollout began on February 7, 2023 and reached every Android client on May 11, 2023.
Most websites never noticed. Anything that matched app installs to clicks did.
The same header, before and after
Before Chrome 110
Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36Chrome 110 and later
Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36What it broke for deferred deep links
A deferred deep link has to survive an install. When the store can carry a token, it does: Google Play and Huawei AppGallery both hand the new app an install referrer, and that settles the match exactly. When it can’t, because the install started from a store search, a sideload or a store without a referrer, all that is left is a guess. Find a recent click from the same network and ask whether it looks like the same phone.
“Looks like” used to lean on the user agent. A click from Android 14 was a poor candidate for a phone that reports Android 15 on its first launch, and a click from a Galaxy was a poor candidate for a Pixel. After the reduction, every Chrome click said Android 10 and K. The version stopped narrowing anything down, and on a busy network, an office or a campus, every Android phone in the building looked like every other one.
Appy reads Android 10; K as what it is: no version and no model. With nothing else on the click, every Android click from the same network looked the same, and Appy couldn’t tell your new user’s phone apart from the others.
The textbook fix counts every click twice
Chrome didn’t delete the information. It moved it into User-Agent Client Hints. A few low-entropy hints come with every request: the browser brand and major version, whether the device is mobile, and the platform name. The two that matter here, Sec-CH-UA-Platform-Version and Sec-CH-UA-Model, are high-entropy. Chrome sends them only after the site asks with an Accept-CH response header, so the first request to a site never carries them.
For data a page needs on the very first load, Google’s Privacy Sandbox guide to the Android reduction (February 27, 2023) points to Critical-CH. The server names the hints it can’t work without; Chrome sees they were missing, drops the response and sends the request again with the hints attached.
“Again” is the catch. For a redirect service, that retry is a second navigation to the same link: every Android tap would be counted twice and leave two click records behind. We would rather do a little more work than put wrong numbers in every customer’s dashboard, so Appy doesn’t use Critical-CH.
What Appy link domains do instead
Two mechanisms: one for the first tap from a browser, one for every tap after it.
Chrome on the phone
acme.appy.to
- 01First tap on a link
GET /spring HTTP/2 Host: acme.appy.to User-Agent: Mozilla/5.0 (Linux; Android 10; K) … Chrome/154.0.0.0 Mobile Safari/537.36 Sec-CH-UA-Mobile: ?1 Sec-CH-UA-Platform: "Android"Low-entropy hints only. The user agent says Android 10, model K.
- 02Hand-off page
HTTP/2 200 Accept-CH: Sec-CH-UA-Platform-Version, Sec-CH-UA-Model Content-Type: text/html; charset=utf-8The page that opens the app or the store. Accept-CH asks for both hints on every later request.
- 03Before the hand-off
const hints = await navigator.userAgentData .getHighEntropyValues(["platformVersion", "model"]) → {platformVersion: "16.0.0", model: "Pixel 9", …} navigator.sendBeacon(…)The page reads the real values, attaches them to this click’s record, then hands off.
- 04Every later tap
GET /summer HTTP/2 Host: acme.appy.to Sec-CH-UA-Platform-Version: "16.0.0" Sec-CH-UA-Model: "Pixel 9"Chrome remembers the request for this origin until the user clears site data, so the hints now travel as headers. No script, no retry.
What the real version and model change
With the real version and model on the click, the click and the phone have to agree on both. The SDK reports the phone’s model from Build.MODEL when the app first opens, so the two can be compared. A Pixel 9 click can no longer be taken for a Galaxy install, and a click from Android 15 no longer fits a phone on Android 16.
The difference shows most on crowded networks. On an office network with forty phones, clicks from other models drop out, and the Pixel’s own click is often the only one left.
All of this is about installs that arrive without a token. On Android, the Google Play and AppGallery install referrers still settle most installs first, and Client Hints are there for the rest. Without a referrer or a click id, Appy can’t verify the install, so with strict attribution on, it counts as organic however well the click fits.
One office network, before and after
A Pixel 9 on a busy office network opens the app for the first time, four minutes after its owner tapped a link. They closed the store page the link opened and installed the app from a Play Store search instead, so no referrer arrives.
iOS froze its own number
Safari reached the same place by another road. Apple’s Safari 26.0 release notes say Safari now reports a frozen OS version in its user agent on iOS 26 and iPadOS 26, the last version released before iOS 26. In practice an iPhone on iOS 26 calls itself “iPhone OS 18_6”. The Version/26.0 token further along the string still follows Safari itself.
Safari doesn’t support User-Agent Client Hints, so there is nothing to ask for. Appy handles it in matching instead: when the phone runs iOS 26 or later, a click that says iOS 18.6 is read as “26 or later”, not as an exact version, because Safari no longer says which one.
If you match installs yourself
None of this is specific to Appy.
Treat “Android 10; K” as unknown
Parse it and set it aside. A frozen value that looks like data does more harm than an empty field.
Ask for hints on the domain that serves the click
Send Accept-CH: Sec-CH-UA-Platform-Version, Sec-CH-UA-Model from your link domain, and read navigator.userAgentData on the first visit, when the headers can’t be there yet.
Count what a retry does before you add Critical-CH
A retried navigation is a second request. If you count requests, deduplicate them or leave the retry out.
Map iOS 18.6 to “26 or later”
Don’t reject an iOS 26 install because its click said 18.6. Treat it as iOS 26 or later, not as an exact version.
Prefer tokens to guesses
Install referrers and click ids beat any user agent. Guess only when nothing deterministic arrived, and say that it is a guess.
Read next
Frequently asked questions
Why does Chrome say Android 10 on every phone?
User-agent reduction. Since Chrome 110, rolled out between February and May 2023, Chrome on Android freezes the OS version at 10 and the device model at K in the User-Agent string. The real values are available through User-Agent Client Hints.
How do I get the real Android version in Chrome?
Ask for the Sec-CH-UA-Platform-Version hint with an Accept-CH response header, or call navigator.userAgentData.getHighEntropyValues(["platformVersion"]) in the page. The header only arrives on requests made after Chrome has seen your Accept-CH.
Does Critical-CH fix the first request?
It gets you the hints by making Chrome repeat the request. That works for ordinary pages. For anything that counts requests, such as redirects or click tracking, the repeat looks like a second visit unless you deduplicate it.
What does Safari report on iOS 26?
A frozen OS version. On iOS 26 and iPadOS 26 Safari reports the last version released before iOS 26, in practice 18_6, while the Version/26 token still follows Safari. Safari doesn’t support User-Agent Client Hints.
Are Android WebViews and in-app browsers reduced too?
Not by default yet. Google has said the default WebView user agent will get the same treatment starting with Android 17, and apps that set their own user agent keep it.
Continue exploring
Deferred Deep Linking Without an SDK: How It Actually Works Under the Hood
You can carry link intent across an app install without embedding an attribution SDK. What actually survives the store boundary on Android and iOS, what breaks, and what each workaround costs in privacy.
Know Which Link Brought Every Install: Appy’s iOS and Android SDK
Appy now follows your links past the app store: installs, in-app events and revenue for every link, QR code and campaign, and new users who open on the right screen.
Deferred Deep Linking: How It Works and When to Use It
Understand how deferred deep linking carries the original link intent across install so new users land on the right in-app screen, not a generic home tab.
Looking for something else? Browse all topics on the blog.
Match installs on evidence, not on “Android 10”
Appy link domains now ask for Client Hints. Deferred deep linking and install attribution come with the SDK on the Enterprise plan, and smart links start free.