Since the introduction of the web, browsers (or other clients) have been using the
user-agent header to identify themselves.
For years, web servers and REST API endpoints could rely on the contents of this string to determine various properties of the visitor's browsing environment, such as browser name, operating system and even hardware architecture.
Over the last couple of years, privacy concerns regarding information sent over the network without the user's consent, have lead browser makers to adopt a minimal-information approach to their user-agent string, effectively reducing the specificity to just basic information about the browser itself.
What if there was a way for the browser (and hence the user) to decide what information to share and for web servers to only request what they need?
Turns out there is a specific Web API for this, the so-called
Client Hints. This API is enabled in Google Chrome since version 89.
The goal of this API is to disallow websites to closely identify a user while still being able to request useful information such as operating system, whether it is a mobile device, and other preferences that require variations in browsing experience. Client hints also try to solve a long-standing problem of the unstructured user-agent string and the complexities of parsing it into useful information.
How does it work?
Client hints are a fine-grained set of request headers the browser can send upon request.
By default, there is only very basic information when sending a request (you can check this for yourself in your browser's web inspector):
GET / HTTP/1.1 Host: example.com Sec-CH-UA: "Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24" Sec-CH-UA-Mobile: ?0 Sec-CH-UA-Platform: "macOS"
As you can see, already some pretty decent info, but rather basic. It reveals that we are running Chrome 119 on macOS from a non-mobile device.
Now, if the web server wishes to receive some more information, it can respond for example:
HTTP/1.1 200 OK Accept-CH: Sec-CH-UA-Full-Version-List
All subsequent requests from the browser will now include the requested header
GET / HTTP/1.1 Host: example.com Sec-CH-UA: "Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24" Sec-CH-UA-Mobile: ?0 Sec-CH-UA-Full-Version-List: "Google Chrome";v="119.0.6045.123", "Chromium";v="119.0.6045.123", "Not?A_Brand";v="126.96.36.199" Sec-CH-UA-Platform: "macOS"
Other examples include
HTTP/1.1 200 OK Accept-CH: Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Model
which results in:
GET / HTTP/1.1 Host: example.com Sec-CH-UA: "Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24" Sec-CH-UA-Mobile: ?0 Sec-Ch-Ua-Arch: "arm" Sec-Ch-Ua-Bitness: "64" Sec-CH-UA-Platform: "macOS"
As you can see, values can also be empty. It is up to the browser to actually provide the requested information or not.
The browser will keep sending this set of headers for the duration of the browsing session or until the set of requested headers is changed by the web server. They will also only be sent to the origin server (in this example: example.com).
The origin server can also respond with a permissions policy that allows the browser to also send certain Client Hints to Cross-Origin requests, for example:
HTTP/1.1 200 OK Accept-CH: Sec-CH-UA-Platform-Version Permissions-Policy: ch-ua-platform-version=(self "downloads.example.com");
Which will make sure that the browser sends the
Sec-CH-UA-Platform-Version header with origin requests to
example.com and also with CORS requests to
As the user-agent header loses its usefulness as a source for browser information, we now have the Client Hints API to provide us with the data we need. Although it is currently only supported by Chromium-based browsers, it is very useful to make an informed decision about the most appropriate content to send in specific end-user environments.
As always, we hope you liked this article, and if you have anything to add, we are available via our Support Channel.