題幹與適用場景
這道通用技術題考察 HTTP 表示、內容協商和共享快取的邊界。重點不是羅列 Accept 標頭,而是證明代理不會把一種語言或編碼的回應錯誤複用給另一個請求,同時讓回退和除錯行為可解釋。
面試官考察什麼
- 能否區分資源 URI 與同一資源的不同表示,並理解主動協商的請求標頭。
- 能否用 Vary 宣告影響回應的請求欄位,推導快取鍵和命中率取捨。
- 能否設計語言、媒體類型和壓縮編碼的確定性回退,避免錯誤的 406 或串內容。
- 能否識別 Cookie、Authorization、使用者特徵和高基數標頭帶來的共享快取與隱私風險。
回答前需要澄清的問題
先確認資源是否公開可共享,變體維度有哪些,是否允許伺服器驅動協商,客戶端能否顯式選擇語言,以及 CDN 是否支援按 Vary 分片。也要確認不支援語言的產品回退、壓縮演算法白名單、回應是否含個人化資料和可接受的快取命中率。
30 秒回答框架
我會把資源 URI 與表示選擇分開建模,按 Accept、Accept-Language 和 Accept-Encoding 的權重做確定性選擇,並在回應中宣告 Content-Type、Content-Language、Content-Encoding 與準確的 Vary。共享快取只複用同一變體鍵;個人化或高基數請求繞過共享快取。無法滿足時按產品規則回退並記錄協商結果,再用帶請求標頭的快取命中、內容校驗和隱私測試驗證。
分步驟深入解答
1. 先定義表示與協商維度
把同一 URI 看成資源的多種表示,例如 HTML 與 JSON、中文與英文、gzip 與 br。只把真正影響回應位元組或語意的請求欄位納入協商;不要因實作方便把任意 Cookie 或完整 User-Agent 都放入維度。伺服器應記錄選擇結果,使相同輸入得到可預測表示。
2. 設計權重、回退與錯誤
解析 Accept 系列中的品質因子和萬用字元,定義伺服器支援清單與優先順序。語言可按區域到基礎語言再到預設語言回退,媒體類型可提供明確的預設表示;只有沒有可接受表示且產品確實需要拒絕時才回傳 406。壓縮編碼只選擇允許的編碼,未知或不安全值不應繞過白名單。
3. 讓快取變體可計算
回應的 Vary 列出參與選擇的請求標頭,例如 Accept, Accept-Language, Accept-Encoding。快取用 URI、方法和這些欄位的正規化值形成變體鍵;正規化包括大小寫、空白和排序,不能把不同寫法無意間擴成無限鍵。若回應依賴授權、會話或使用者 Cookie,應使用私有快取或明確禁止共享快取。
4. 控制命中率、隱私與資源數量
每增加一個維度都會拆分快取並降低命中率,Accept-Language 還可能成為瀏覽器指紋訊號。公開頁面可只支援有限語言集合,把長尾映射到預設語言;個人化資料採用 private、短 TTL 或不快取。不要用 Vary: * 掩蓋不清楚的依賴,因為它會讓共享快取無法複用。
5. 設計可觀測性與驗證
日誌記錄正規化協商輸入、選中的表示、Vary、快取命中狀態和回退原因,但不要記錄完整敏感 Cookie。測試每個變體的回應標頭、壓縮解碼、跨 CDN 命中和未命中,特別驗證先請求中文再請求英文不會串內容。線上排查時對比 Age、快取狀態、Content-* 標頭和實際位元組,並用固定請求矩陣重放。
高品質示範回答
我會先列出有限的表示集合,再根據 Accept、Accept-Language 和 Accept-Encoding 的品質因子做確定性選擇。回應同時返回 Content-Type、Content-Language、Content-Encoding 和準確的 Vary,快取鍵只包含這些正規化維度。授權、會話和高基數特徵不進入共享快取;公開頁面的語言長尾回退到預設語言。沒有可接受表示時按產品規則回傳 406 或明確預設值。日誌記錄選擇、回退和命中狀態而不記錄敏感 Cookie,並用「先中文後英文、先 br 後 gzip」矩陣驗證 CDN 不會複用錯誤變體。
常見錯誤
- 只設定 Content-Language,不在回應中宣告真正影響選擇的 Vary。
- 把完整 Cookie、User-Agent 或任意請求標頭放進快取維度,造成命中率崩潰。
- 看到不支援的語言就回傳 406,沒有定義區域到基礎語言和預設語言回退。
- 讓共享快取複用帶 Authorization 或會話資料的回應。
- 忽略壓縮編碼協商,回傳客戶端無法解碼的 Content-Encoding。
- 只看狀態碼,不對比 Content-* 標頭、快取狀態和實際回應位元組。
追問及應對
為什麼 Vary 不能只寫 Accept-Language?
如果回應也受媒體類型或壓縮編碼影響,快取還需要知道這些維度。遺漏會讓共享快取把一個表示交給不相容的請求,產生串內容或解碼失敗。
Vary: * 什麼時候有用?
它表示選擇依賴未列出的資訊,通常會阻止共享快取複用。它可以作為保守保護,但不能替代梳理真實依賴和改用 private 快取。
如何降低 Accept-Language 帶來的鍵爆炸?
把客戶端語言映射到有限支援集合,按區域、基礎語言和預設語言回退;必要時用顯式 URL 或使用者選擇覆蓋自動協商,並監控每種變體的命中率。
協商失敗時應該回傳 406 還是預設表示?
取決於介面契約。機器介面通常需要明確錯誤,網頁可回退到預設語言或媒體類型;兩者都應在文件、回應標頭和日誌中保持一致,不能靜默回傳錯誤語意。