Googlebooks are built on the Android technology stack and paired with desktop
foundations from ChromeOS. Most web applications should run the same on
Googlebooks as on any other desktop device and will see the same User Agent
string as ChromeOS:

```text
Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0; Safari/537.36
```

To identify Googlebooks more accurately, for example, for analytics, or to
direct users to download your compatible Android app, you need to request two
[User-Agent Client Hints](https://web.dev/articles/migrate-to-ua-ch): the
`Platform` and the `Mobile` client hints. Both of these are
[low entropy hints](https://developer.mozilla.org/docs/Web/HTTP/Guides/Client_hints#low_entropy_hints),
allowing you to get them without additional requests or permissions, just like
you do with the User-Agent string. You can incorporate them directly into
your existing detection.

| UA-CH Header | Value for Googlebooks |
|---|---|
| `Sec-CH-UA-Platform` | `"Android"` |
| `Sec-CH-UA-Mobile` | `?0` (False) |

To detect this in the browser, use the `navigator.userAgentData` API:

```javascript
const isGooglebook = window?.navigator?.userAgentData?.platform === 'Android' && window?.navigator?.userAgentData?.mobile === false;
```

Because of
[User-Agent reduction](https://developer.mozilla.org/docs/Web/HTTP/Guides/User-agent_reduction),
a Googlebook specific platform was not added to the `Platform` client hint or
user agent string, so to ensure there was a distinction between Googlebooks and
the previous version of ChromeOS, the client hint was changed to `"Android"`,
reflecting that Googlebooks are compatible with Android apps.

> [!NOTE]
> **Note:** `navigator.platform` returns `Linux x86_64` regardless of the underlying architecture or platform [as part of](https://www.chromium.org/updates/ua-reduction/#reduced-navigatorplatform-values-for-all-versions) User-Agent reduction. If you need the architecture the user is running, the `Sec-CH-UA-Arch` client hint returns the correct device architecture, but it is a [high entropy hint](https://developer.mozilla.org/docs/Web/HTTP/Guides/Client_hints#high_entropy_hints).

## Server-side detection

Because these are both low entropy client hints, they'll be sent to your server
on initial request from the client and you can pick them up in one of the
following ways:

### Node.js

```javascript
const platform = req.headers['sec-ch-ua-platform'] || 'Not Sent';
const mobile = req.headers['sec-ch-ua-mobile'] || 'Not Sent';

const cleanPlatform = platform.replace(/"/g, '');
const isGooglebook = cleanPlatform === 'Android' && mobile === '?0';
```

### Java

```java
String platform = exchange.getRequestHeaders().getFirst("Sec-CH-UA-Platform");
 if (platform == null) platform = "Not Sent";

String mobile = exchange.getRequestHeaders().getFirst("Sec-CH-UA-Mobile");
if (mobile == null) mobile = "Not Sent";

String cleanPlatform = platform.replace("\"", "");
boolean isGooglebook = cleanPlatform.equals("Android") && mobile.equals("?0");
```

### Ruby

    mobile = req['Sec-CH-UA-Mobile'] || 'Not Sent'

    clean_platform = platform.gsub('"', '')
    is_googlebook = clean_platform == 'Android' && mobile == '?0'

### Python

```python
platform = self.headers.get('Sec-CH-UA-Platform', 'Not Sent')
mobile = self.headers.get('Sec-CH-UA-Mobile', 'Not Sent')

clean_platform = platform.replace('"', '')
is_googlebook = clean_platform == 'Android' and mobile == '?0'
```

### PHP

    $mobile = $_SERVER['HTTP_SEC_CH_UA_MOBILE'] ?? 'Not Sent';

    $cleanPlatform = str_replace('"', '', $platform);
    $isGooglebook = ($cleanPlatform === 'Android' && $mobile === '?0');

### Go

```go
platform := r.Header.Get("Sec-CH-UA-Platform")
if platform == "" {
  platform = "Not Sent"
}
mobile := r.Header.Get("Sec-CH-UA-Mobile")
if mobile == "" {
  mobile = "Not Sent"
}

cleanPlatform := strings.ReplaceAll(platform, "\"", "")
isGooglebook := cleanPlatform == "Android" && mobile == "?0"
```

### Other useful client hints

While the `Platform` and `Mobile` client hints are enough to let you know your
users are using a Googlebook, you may want more information about their device.
The
`[Sec-CH-UA](https://developer.mozilla.org/docs/Web/HTTP/Reference/Headers/Sec-CH-UA)`
client hint returns a comma-separated list of the major browser version and
other brands associated with the device, which is more straightforward to parse
than doing so from the User-Agent string. There's also the `Form Factors` client
hint
([`Sec-CH-UA-Form-Factors`](https://developer.mozilla.org/docs/Web/HTTP/Reference/Headers/Sec-CH-UA-Form-Factors)),
a
[high entropy hint](https://developer.mozilla.org/docs/Web/HTTP/Guides/Client_hints#high_entropy_hints),
that provides a comma-separated list of form factors for the given device
(Desktop, Tablet, Mobile, Automotive, and so on). While this is often a single
form factor, all applicable form factors could be included, so a detachable
device may return `"Desktop", "Tablet"`, for example. If it's important to know
precisely what form factor your users are on, and not only that it's not a
mobile Android device, you can use the
[`Critical-CH`](https://developer.mozilla.org/docs/Web/HTTP/Reference/Headers/Critical-CH)
header:

```text
GET / HTTP/1.1
Host: example.com

HTTP/1.1 200 OK
Content-Type: text/html
Accept-CH: Sec-CH-UA-Form-Factors
Vary: Sec-CH-UA-Form-Factors
Critical-CH: Sec-CH-UA-Form-Factors
```

If the client would have included a critical hint after processing the
`Accept-CH` header, it will retry the request. This lets you serve an optimized
page instead of the initial response. Because this adds an extra round trip, use
it only for meaningful page changes that require the critical information.