先判斷重新驗證是否值得做
合作夥伴程式可能每日多次索取同一份公開商品目錄,因為它要知道價錢或描述有否改變。若回應很大,每次都重送未改動內容會浪費傳輸。ETag 是伺服器為某一個已選定回應版本提供的驗證標記。客戶端保存回應內容及標記;下一次 GET 時把舊標記放進 If-None-Match。若目前版本相符,伺服器可回傳沒有本文的 304 Not Modified;否則正常傳回目前內容。這種做法只有在客戶端確實保留舊本文、並能處理兩種結果時才有價值。
不要一開始便承諾節省某個百分比。先找出內容穩定、只供讀取,而且同一客戶端經常重複索取的端點。有實際觀察資料時,記錄回應大小、更新頻率和客戶端行為。若回應很小或每次請求都變,驗證標記可能只增加複雜度。若合作夥伴根本不保存舊內容,收到 304 時便沒有本文可用。真正的業務問題是能否在保持正確性的同時減少不必要傳輸,而不是讓 HTTP 狀態碼看起來更進階。缺乏量度時,節省幅度只可當作假設,再用代表性的客戶端測試。
必須清楚定義單一網址會選出甚麼回應。商品目錄端點可能按語言、貨幣、市場、查詢篩選條件或 API 版本而改變。驗證標記應對應這些請求細節選出的版本;英文回應的標記不能授權重用中文回應,某貨幣的標記亦不能驗證另一貨幣。若以請求標頭決定版本,回應中的 Vary 應告訴快取哪些標頭重要。若以查詢參數決定版本,就要一致地把它們納入資源身份並測試各種組合。304 只確認某個已儲存的選定回應仍可使用,並非宣稱整個商品資料庫完全沒有改動。
準確處理條件式請求
RFC 9110 把 ETag 定義為帶引號、內容不供客戶端解讀的標記。伺服器可根據回應版本等方法產生它,但凡影響輸出的資料改變,標記也應相應改變;客戶端則原樣保存及送回。強標記須符合標準所要求的位元組等價條件;以 W/ 開頭的弱標記容許在快取驗證時比較語義等價的回應。若伺服器達不到強標記要求,就必須標明為弱。若回應同時取決於庫存、語言、促銷安排及其他資料,不能隨便用某一資料庫紀錄的更新時間當作整份回應的標記。標記必須描述端點實際交付的內容。
客戶端之後以 If-None-Match 隨 GET 送回舊標記。根據 RFC 9110,這項條件使用弱比較,因此弱標記也可驗證快取內容。若與目前選定版本相符、條件結果為 false,GET 或 HEAD 應得到 304;若不相符,伺服器就繼續傳送目前內容。當 If-None-Match 和 If-Modified-Since 同時出現,標準優先處理前者,在該情況不再用日期條件。舊客戶端兩者並用時,這個次序尤其重要。不要把讀取時的重新驗證,與修改資料時用 If-Match 防止覆蓋更新的問題混為一談;後者使用強比較。
304 回應沒有訊息本文。RFC 9110 亦要求它包含與同等 200 回應相關的元資料,例如適用的 ETag、Vary 及 Cache-Control,讓客戶端或中介快取更新已存回應的標頭,並沿用舊本文。不要用空的 JSON 物件冒充 304,也不要因為某個資料庫欄位未改,就把已改變的回應判成 304。版本改變時,應傳回完整新本文及新標記;首次請求沒有儲存本文時,亦應正常傳送內容。這些情況需要同時測試伺服器和客戶端,因為表面上請求成功,畫面仍可能顯示過時商品。
實作檢討時,要問多個服務共同產生回應的情況下,伺服器如何計算標記。以最終序列化回應計算雜湊是一種設計,但有運算成本,並須考慮內容編碼及輸出是否穩定。另一方法是儲存版本計數,但所有會影響回應的變動都必須可靠地更新它。具體選擇取決於系統架構;不變原則是客戶端可見的選定版本已改變時,標記不能仍聲稱兩者等價。測試應改動每個資料來源,包括價格資訊流及翻譯欄位,不能只改主要商品紀錄。
讓快取規則配合資料性質
ETag 本身不決定回應可以在多久之內毋須聯絡伺服器而直接重用;這由 Cache-Control 負責。RFC 9111 說明,沒有附加參數的 no-cache 容許儲存回應,但再次使用前必須成功驗證;no-store 則要求快取不要儲存。若公開目錄經常改動,而商戶希望每次重用前檢查,要求重新驗證的規則可能合適。若內容可安全地在限定時間內沿用,新鮮期可以進一步減少請求。應與商品負責人按內容性質決定:價格及供貨顯示,可能與較穩定的分類描述需要不同規則。
不要在這個公開端點混入個別顧客資料。若回應包含個人折扣、帳戶資格或未公開批發價,它便不再是所有人共用的公開版本,需要另外設計私隱和快取規則,而非只在共用資訊流加一個 ETag。也要定義商品下架時的處理:舊標記不能令已刪除資源彷彿仍然存在。快取可能保存舊本文,但來源伺服器須按照 API 合約回應目前資源狀態。只有在回應身份及存取控制正確的前提下,重新驗證才真正有助保持一致。
也要考慮中介層。合作夥伴可能經 CDN、反向代理或自身閘道呼叫 API。應驗證選定版本、ETag、Vary 及 Cache-Control 在每一層都保持正確,304 亦只與相應的已存 200 本文配對。若代理移除了 Vary,或把某語言的本文給另一語言,即使來源程式正確,顧客仍會看錯資料。記錄足以分辨來源狀態、快取狀態和客戶端可見結果的資料,同時避免記下機密憑證。公開端點應沿合作夥伴實際使用的路徑測試;只測本地處理函式,會錯過最初想改善的快取互動。
演練資料變與不變的情況
可用受控例子演練。假設公開端點回傳某市場的英文商品目錄。第一次 GET 帶回本文與標記 A,客戶端兩者都保存。選定版本未變時,第二次帶 If-None-Match A 的 GET 應收到沒有本文的 304,客戶端顯示舊本文。團隊接着修改回應內可見的商品描述;下一次條件式 GET 應帶回新本文與標記 B。若只修改完全不影響回應的內部欄位,標記可以不變,但前提是驗證方法確實準確反映回應版本。這些是測試資料的預期結果,並非正式環境已觀察到的時間或節省數字。
發布前要擴充測試矩陣。分別以沒有標記、現行標記、過時標記、支援時的弱標記,以及另一語言或查詢版本的標記請求同一資源。測試首次使用而未保存本文的客戶端、本文被清走的客戶端,以及經代理的路徑。逐項核對狀態碼、有否本文、元資料及最後顯示的資料。若客戶端收到 304,卻遺失本地舊本文,便應重新作無條件請求,而非顯示空白。這項恢復安排屬於應用設計決定,須寫進合作夥伴整合指引並測試。
首輪可只在一個端點及一個願意配合的客戶端推出。記錄真實的 200 與 304、選定的資源版本、異常 304 及客戶端錯誤。304 比例上升,若合作夥伴同時看到舊價錢或不斷重試,就不能算服務改善。在宣稱效率成果前,應在相近流量組合下比較傳輸位元組及顧客實際看到的正確性。若監測工具暫時不可用,仍可依靠正確性測試和合作夥伴回饋作審慎推出,但不要作量化成效聲稱。下一步是畫出端點所有回應版本,並經接近正式環境的閘道測試一次「未變至已變」流程。
進行前先確認
- 每個 ETag 是否對應語言等條件所選出的正確回應?
- 條件吻合的 GET 是否回傳沒有本文、但元資料正確的 304?
- 客戶端遺失已存本文後收到 304,能否重新取得完整回應?


