解決方案
HTTP 的 Vary 標頭會告訴快取哪些請求標頭可能影響回應,因此快取不會把瀏覽器要的 HTML 回應給要求 JSON 的 API 用戶端。Cloudflare 引用一份針對近 5 萬個熱門網站、超過 1.2 億個回應的外部分析,發現近 3,000 個回應會因四個以上欄位而變動,有些甚至因 10 個、23 個,甚至 47 個欄位而變動。Vary 本身正確,但若照字面解讀,可能讓快取幾乎失去價值:三個欄位各有十個值,就已經可能產生多達 1,000 種變體,而像 Accept-Language 這類標頭,即使順序或格式不同,對來源伺服器而言也可能意義相同。
結果就是一個「完全正確卻幾乎永遠冷快取」的快取:位元組完全相同的回應散落在許多幾乎不會被重複使用的項目中,彼此驅逐,並把更多請求推回來源伺服器。既有的變通做法——完全繞過快取、在 Worker 中重複實作內容協商邏輯,或像圖片專用 Vary 這類範圍狹窄的功能——各自都放棄了快取、重複了應用程式邏輯,或只涵蓋單一情境。
現在,快取規則讓來源伺服器繼續宣告 Vary 標頭,同時由規則設定針對每個標頭決定:要將值正規化為一組小型等價集合、直接傳遞精確值,或對無法預測的標頭繞過快取。兩個 Accept-Language 請求——en-US, fr;q=0.8 與 fr;q=0.8, en-GB——都偏好英文,現在在正規化模式下可以共用同一個快取項目,而不會變成兩種變體;而必須精確變動的標頭則可使用直接傳遞。此功能即日起在 Free 到 Enterprise 方案中提供。
達成了什麼
將「哪些部分會變動」與「變動幅度有多少才算數」分開處理
為何有效
將 Accept 與 Accept-Language 依品質值排序,並裁剪為一組設定的格式,可把位元組不同但語意相同的請求收斂為同一個項目。
將正規化/直接傳遞/繞過的選擇設為依標頭決定,而非全域決定,讓操作者能把 Cookie 視為無界(繞過),同時把 Accept-Language 視為小型封閉集合(正規化)。
將正規化後的標頭值轉送至來源伺服器,而不只是用於快取鍵,可讓來源伺服器的回應選擇與其被儲存為哪個快取變體保持一致。
Vary: * 仍然一律繞過快取,因為它可能表示 HTTP 訊息以外的因素(例如用戶端 IP)主導了回應,而這是任何標頭層級規則都無法安全正規化的。
可套用的啟示
當一個正確性機制膨脹成組合爆炸式的浪費時,找出你把「不同的位元組」與「不同的意義」當成同一件事的地方,並為操作者提供各自獨立的控制旋鈕。
後續發展
Cloudflare 表示,正在評估已過期的 IETF Availability Hints 草案中的構想,是否能讓來源伺服器直接描述其支援的表示形式,以減少手動設定快取規則應正規化哪些格式與語言的工作。該文章將目前的正規化/直接傳遞/繞過三分法定位為填補既有選項(完全繞過、自訂快取鍵,或基於 Worker 的內容協商邏輯)之間的缺口,而非最終設計。
追溯依據
參考來源
- We just shipped support for the ugliest part of HTTP: Vary The Cloudflare Blog · blog.cloudflare.com ·