‼️ Note: there is an updated version of this article here.
User-Agent Client Hints (UA-CH) has been hot in the Chrome camp lately. Motivated by the sudden urge to deprecate the User-Agent request header due to privacy concerns. With Chrome version 84 and higher, the plumbing with delegation through feature- or permission policies is implemented.
Delegating UA-CH to a third party server is similar to how delegating other client hints work, but with some caveats.
navigator.userAgentData.getHighEntropyValues() is not protected by any opt-in, permission policy or user interaction…
To delegate UA-CH to third parties the steps are as follows:
1. Opt-in to Receive UA-CH
The server has to announce support for client hints. For UA-CH this is not any different than for other hints. The server simply responds with an
Accept-CH header listing the hints supported:
This will ask the user-agent to send the requested hints to all subsequent requests to the same origin who made the request.
But this is not what we want… If the origin is
foo.com, we want the hints to be sent to
In order to do that, we need to make sure the browser sends the hints to
bar.foo.com too. This is done through a feature policy header:
2. Delegate Hints Using Feature Policy
At the time of writing, the mechanism for enabling or disabling features in browsers is called Feature Policy. This will at some point in the future change name to Permission Policy
Note that the hints
ch-ua does not need to be delegated through feature policies. These headers are available by default and are meant to make an alternative to the
To delegate the User-Agent Client Hints to a 3rd party we need to add the
Feature-Policy header to the response along with the
Feature-Policy: ch-ua-arch https://bar.foo.com;ch-ua-model https://bar.foo.com;ch-ua-platform https://bar.foo.com;ch-ua-platform-version https://bar.foo.com;ch-ua-full-version https://bar.foo.com
Here’s the first caveat. Note that the hints are prefixed with
ch-. Even if the hint is named
ua-arch in the
Accept-CH header, it is delegated to the third party as
The 3rd party does not have to announce support for hints or opt in to receiving hints. All hints are sent regardless.
The second caveat is that the hints specified in the policy are case sensitive and must be lowercase! This is true for all client hints, not only UA-CH.
3. Read the Request Headers at the 3rd Party
By now the 3rd party should receive the hints specified in the policy as request headers.
Here’s another caveat. The UA-CH header names are now prefixed yet another time. Now with
sec-. The hint
ua-arch we opted in for in step 1 reveals itself as
sec-ch-ua-arch. Yea, I know… Here’s the reason.
The request headers to the 3rd party may now look something like this:
sec-ch-ua-full-version: "85.0.4156.0" sec-ch-ua-arch: "" sec-ch-ua-platform: "Mac OS X" sec-ch-ua-platform-version: "10_13_6" sec-ch-ua-model: "" sec-ch-ua-mobile: ?0 sec-ch-ua: "\\Not;A\"Brand";v="99", "Google Chrome";v="85", "Chromium";v="85"
How does UA-CH work?
I put together this demo on glitch.com: https://glen-wistful-protoceratops.glitch.me/
Make sure to test in a supporting browser, like Chrome v 84 or higher.
You’ll notice that hints are sent on the first request to the 3rd party iframe.
☝🏻 make sure to open this demo in a separate window https://glen-wistful-protoceratops.glitch.me/