遷移至使用者代理程式用戶端提示

採取以下策略,將網站從仰賴使用者代理程式字串遷移至新的 User-Agent Client Hints。

User-Agent 字串是瀏覽器中重要的被動指紋介面,而且難以處理。然而,收集和處理使用者代理程式資料的方法有很多種,因此您需要的是能找出更好的解決方案。「使用者代理程式用戶端提示」提供一種明確的方式,可讓您宣告需要使用者代理程式資料,以及採用易於使用格式傳回資料的方法。

本文將逐步說明如何稽核您的使用者代理程式資料存取權,並將使用者代理程式字串的使用情形遷移至 User-Agent Client Hints。

稽核使用者代理程式資料的收集與使用方式

和任何形式的資料收集行為一樣,請務必先瞭解收集資料的「原因」。無論您是否打算採取任何動作,第一步都是瞭解您使用使用者代理程式資料的位置和原因。

如果您不知道使用者代理程式資料的用途或於何處,請考慮在前端程式碼搜尋 navigator.userAgent 使用,並透過後端程式碼使用 User-Agent HTTP 標頭。您也應該檢查前端程式碼,使用已淘汰的功能,例如 navigator.platformnavigator.appVersion

從函式的角度來看,您可以思考程式碼中錄製或處理的任何位置:

  • 瀏覽器名稱或版本
  • 作業系統名稱或版本
  • 裝置品牌或型號
  • CPU 類型、架構或位元程度 (例如 64 位元)

此外,您也可以使用第三方程式庫或服務處理使用者代理程式。在此情況下,請檢查確認其是否正在更新,以支援「User-Agent Client Hints」。

您是否只使用基本使用者代理程式資料?

預設的「使用者代理程式用戶端提示」包括:

我們提議的簡化版使用者代理程式字串,也會以回溯相容的方式保留這些基本資訊。舉例來說,字串會包含 Chrome/90.0.0.0,而非 Chrome/90.0.4430.85

如果您只檢查使用者代理程式字串中的瀏覽器名稱、主要版本或作業系統,那麼您的程式碼會繼續運作,但您可能會看到淘汰警告。

雖然您可以且應該遷移至 User-Agent Client Hints,但可能有舊版程式碼或資源限製而無法執行上述操作。以這種回溯相容的方式減少使用者代理程式字串中的資訊,旨在確保雖然現有程式碼接收的資訊較少,但仍應保有基本功能。

策略:隨選用戶端 JavaScript API

如果您目前使用的是 navigator.userAgent,請先改用 navigator.userAgentData,再改回剖析使用者代理程式字串。

if (navigator.userAgentData) {
  // use new hints
} else {
  // fall back to user-agent string parsing
}

如要檢查行動裝置或電腦,請使用布林值 mobile 值:

const isMobile = navigator.userAgentData.mobile;

userAgentData.brands 是具有 brandversion 屬性的物件陣列,瀏覽器可以列出與這些品牌的相容性。您可以直接以陣列的形式存取該陣列,或是建議使用 some() 呼叫來檢查特定項目是否存在:

function isCompatible(item) {
  // In real life you most likely have more complex rules here
  return ['Chromium', 'Google Chrome', 'NewBrowser'].includes(item.brand);
}
if (navigator.userAgentData.brands.some(isCompatible)) {
  // browser reports as compatible
}

如果您需要其中一個更詳細、高熵的使用者代理程式值,就必須指定這個值,並檢查傳回的 Promise 中的結果:

navigator.userAgentData.getHighEntropyValues(['model'])
  .then(ua => {
    // requested hints available as attributes
    const model = ua.model
  });

如果您想從伺服器端處理改為用戶端處理作業,也建議您使用這項策略。JavaScript API 不需要存取 HTTP 要求標頭,因此可以隨時要求使用者代理程式值。

策略:靜態伺服器端標頭

如果您在伺服器上使用 User-Agent 要求標頭,且整個網站對這些資料的需求相對一致,就可以在回應中將所需的用戶端提示指定為靜態集。這種方法相對簡單,因為通常只需要在一個位置設定。舉例來說,您可能已在網路伺服器設定中新增標頭、託管設定,或是網站使用的架構或平台的頂層設定。

如要根據使用者代理程式資料轉換或自訂提供的回應,請考慮採用這項策略。

瀏覽器或其他用戶端可能會選擇提供不同的預設提示,因此建議您指定您需要的所有內容 (即使通常為預設提示)。

舉例來說,目前的 Chrome 預設設定會顯示為:

⬇️ 回應標頭

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA

如果您也想在回應中收到裝置型號,可以傳送:

⬇️ 回應標頭

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA

在伺服器端處理這項作業時,請先檢查是否已傳送所需的 Sec-CH-UA 標頭,如果沒有,則改用 User-Agent 標頭剖析。

策略:將提示委派給跨來源要求

如果要求跨來源或跨網站子資源需要在其要求中傳送 User-Agent Client Hints,則需要使用權限政策明確指定所需的提示。

舉例來說,假設 https://blog.site 託管 https://cdn.site 上的資源,該資源可以傳回針對特定裝置最佳化的資源。https://blog.site 可以要求 Sec-CH-UA-Model 提示,但需要使用 Permissions-Policy 標頭明確委派給 https://cdn.site。如需政策控管提示清單,請參閱「用戶端提示基礎架構草稿

⬇️ 回覆blog.site委派提示的回覆

Accept-CH: Sec-CH-UA-Model
Permissions-Policy: ch-ua-model=(self "https://cdn.site")

⬆️ 要求 cdn.site 上的子資源並附上委派提示

Sec-CH-UA-Model: "Pixel 5"

您可以為多個來源指定多個提示,不限於 ch-ua 範圍:

⬇️ 回覆blog.site將多個提示委派給多個來源

Accept-CH: Sec-CH-UA-Model, DPR
Permissions-Policy: ch-ua-model=(self "https://cdn.site"),
                    ch-dpr=(self "https://cdn.site" "https://img.site")

策略:將提示委派到 iframe

跨來源 iframe 的運作方式與跨來源資源類似,但您要在 allow 屬性中指定要委派的提示。

⬇️ 來自「blog.site」的回覆

Accept-CH: Sec-CH-UA-Model

↪️ blog.site 的 HTML

<iframe src="https://widget.site" allow="ch-ua-model"></iframe>

⬆️ 向widget.site提出要求

Sec-CH-UA-Model: "Pixel 5"

iframe 中的 allow 屬性會覆寫 widget.site 可能傳送的任何 Accept-CH 標頭,因此請確認您已指定 iframe 網站所需的所有項目。

策略:動態伺服器端提示

比起網站的其他部分,如果您需要更多使用者歷程中的特定提示,則可選擇隨選要求這些提示,而不是以靜態方式在整個網站上要求這些提示。這的管理方式較為複雜,但如果您已為每條路徑設定不同的標頭,或許會可行。

需要注意的是,每個 Accept-CH 標頭的執行個體將有效覆寫現有集合。因此,如果您動態設定標頭,則每個頁面都必須要求完整的必要提示。

舉例來說,假設您可以在網站上有一個區塊,在其中提供與使用者作業系統相符的圖示和控制項。為此,建議您額外提取 Sec-CH-UA-Platform-Version,以提供適當的子資源。

⬇️ 「/blog」的回應標頭

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA

⬇️ 「/app」的回應標頭

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA

策略:第一次要求時需要伺服器端提示

在某些情況下,您需要先處理第一個要求的預設提示組合,還需要其他提示組合。不過,這個情況可能較為罕見,因此請務必仔細查看原因。

第一個要求實際上是指該瀏覽工作階段中傳送的第一個頂層要求。預設的提示組合包含主要版本的瀏覽器名稱、平台和行動指標。所以這個問題是 是否需要在初始網頁載入時擴充資料?

有兩種方式可以提供第一個要求的額外提示。首先,您可以使用 Critical-CH 標頭。這與 Accept-CH 的格式相同,但會指示瀏覽器在傳送第一則訊息時,應在沒有重要提示的情況下立即重試要求。

⬆️ 初始要求

[With default headers]

⬇️ 回應標頭

Accept-CH: Sec-CH-UA-Model
Critical-CH: Sec-CH-UA-Model

🔃? 瀏覽器重試時,會以額外標頭重試初始要求

[With default headers + …]
Sec-CH-UA-Model: Pixel 5

這會導致第一次要求重試的負擔,但實作成本相對較低。您只要傳送額外的標頭,瀏覽器就會完成其餘作業。

如果確實需要在第一次載入頁面時提供額外提示,用戶端提示可靠性提案會制定路徑,以便在連線層級設定中指定提示。這樣就能利用 TLS 1.3 的應用程式層通訊協定設定(ALPS) 擴充功能,在 HTTP/2 和 HTTP/3 連線上啟用這種提早傳遞提示。這目前仍在早期階段,但如果您主動管理自己的傳輸層安全標準 (TLS) 和連線設定,這就是決定做出貢獻的理想時機。

策略:舊版支援

網站上可能含有仰賴 navigator.userAgent 的舊版或第三方程式碼,包括會減少的使用者代理程式字串部分。長期而言,您應規劃改用對等的 navigator.userAgentData 呼叫,但有暫時的解決方案。

UA-CH retrofill 是小型程式庫,可讓您將 navigator.userAgent 改為根據要求的 navigator.userAgentData 值建構的新字串。

舉例來說,以下程式碼會產生使用者代理程式字串,額外納入「模型」提示:

import { overrideUserAgentUsingClientHints } from './uach-retrofill.js';
overrideUserAgentUsingClientHints(['model'])
  .then(() => { console.log(navigator.userAgent); });

產生的字串會顯示 Pixel 5 模型,但由於未要求 uaFullVersion 提示,因此仍會顯示縮減的 92.0.0.0

Mozilla/5.0 (Linux; Android 10.0; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.0.0 Mobile Safari/537.36

進一步支援

如果這些策略不適用於您的用途,請在 Privacy-sandbox-dev-support 存放區中討論,我們將一同探索您的問題。

相片來源:Ricardo Rocha,位於 Unsplash