User-Agent Client Hints (UA-CH) provide web developers with an explicit, privacy-preserving alternative to the legacy User-Agent string. Instead of exposing full hardware, architecture, and operating system build numbers on every HTTP request, Chromium browsers transmit high-entropy device attributes only when servers explicitly request them via Accept-CH. Furthermore, when embedded subresources or third-party iframes require client hints, the modern Permissions-Policy HTTP header governs their delegation. Misconfiguring these security headers risks passive cross-site fingerprinting or silent header stripping in Google Chrome.
Understanding Low-Entropy vs High-Entropy Client Hints
Chromium categorises client hints into two distinct privacy tiers:
- Low-Entropy Hints (Default): Sent automatically on every outgoing HTTP request without requiring an
Accept-CHdeclaration. These includeSec-CH-UA(browser brand and major version),Sec-CH-UA-Mobile(boolean indicator), andSec-CH-UA-Platform(high-level OS brand, e.g., “Windows”). - High-Entropy Hints (Opt-In): Disclose granular system details capable of fingerprinting individual users across browsing sessions. These include
Sec-CH-UA-Arch(CPU architecture, e.g., “x86”),Sec-CH-UA-Bitness(“64”),Sec-CH-UA-Model(device model),Sec-CH-UA-Full-Version-List, andSec-CH-UA-Platform-Version(precise Windows 11 build number).
Configuring Accept-CH and Permissions-Policy Headers
To request high-entropy hints from Google Chrome for the top-level origin and explicitly delegate them to trusted third-party origins (such as analytics or CDN subdomains), configure both Accept-CH and Permissions-Policy:
# Request high-entropy hints from Chromium clients
Accept-CH: Sec-CH-UA-Platform-Version, Sec-CH-UA-Model, Sec-CH-UA-Arch
# Restrict delegation to self and trusted domains only
Permissions-Policy: ch-ua-platform-version=(self "https://cdn.example.com"), ch-ua-model=(self), ch-ua-arch=(self)
Here is an example Nginx configuration implementing strict client hint delegation:
server {
listen 443 ssl http2;
server_name portal.example.com;
# Opt into high-entropy platform version and CPU architecture hints
add_header Accept-CH "Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch" always;
# Securely delegate client hints using Permissions-Policy syntax
add_header Permissions-Policy "ch-ua-platform-version=(self "https://telemetry.example.com"), ch-ua-arch=(self)" always;
# Enforce browser cache partitioning for privacy headers
add_header Critical-CH "Sec-CH-UA-Platform-Version" always;
location / {
proxy_pass http://internal_portal_upstream;
proxy_set_header Host $host;
}
}
Critical-CH: Enforcing Immediate Header Delivery on First Navigation
Because HTTP request headers are emitted before Chrome receives the server’s response headers, the initial navigation request lacks high-entropy hints. The Critical-CH response header instructs Chrome to restart the navigation handshake immediately if the specified hints were absent from the initial request:
Critical-CH: Sec-CH-UA-Platform-Version
When Chrome receives Critical-CH on first contact, it aborts the unannotated request and immediately redispatches the GET request containing the Sec-CH-UA-Platform-Version header, guaranteeing that your edge routing receives correct OS version data without client-side redirects.
Verifying Client Hints and Permissions Delegation in DevTools
- Open Google Chrome and navigate to your application.
- Press F12 to open Chrome DevTools and select the Network tab.
- Click on the primary HTML document request in the list and inspect the Response Headers to confirm that
Accept-CH,Permissions-Policy, andCritical-CHare returned. - Inspect subsequent asset requests to
https://telemetry.example.com; confirm that the request contains the delegatedSec-CH-UA-Platform-Version: "15.0.0"header. - Navigate to the Application tab, scroll down to Background Services, and select Permissions Policy. Verify that each
ch-ua-*feature lists only the authorised origin whitelist.