當 Nginx 使用 proxy cache 的文件做爲響應時,它會更新其中的一些內容,好比 Date 響應頭;但大部分響應頭都不會獲得更新,好比 Expires 和 Cache-Control。衆所周知,Cache-Control 能夠經過 max-age=xxx 或者 s-maxage=xxx 指令設置緩存的有效時間。跟 Expires 響應頭不一樣,這一時間是相對的。假設上游服務器返回 Cache-Control: public; max-age=3600
,那麼 Nginx 會緩存該響應一小時。若是在這一小時到期以前,Client 訪問了 Nginx,它會獲取到一樣的 Cache-Control 響應頭,所以會再緩存多一小時。因此整體上該響應會被緩存兩小時。web
這聽起來很讓人驚訝。但仔細想一想,其實也不算什麼嚴重的問題。首先,當咱們設置 max-age=3600
時,大多數狀況下並不要求其嚴格地在一小時後過時。其次,這個算是通常的多層緩存固有的弊端:緩存數據的最大過時時間,取決於各級緩存 TTL 的總和。若是想要避免,你能夠選擇根據外層數據剩下的 TTL 設置當前 TTL;或者提供主動 purge 的操做,從最內層開始逐層清理數據。瀏覽器
固然,某些時候下,這一行爲會帶來一些問題。舉個例子,假設咱們開啓了 proxy_cache_use_stale,在上游服務器出問題時使用過時的內容代替正常的響應。這種狀況下,緩存只是做爲一個臨時救急的方案使用,咱們並不但願 Client 多緩存更多的時間。不然會有上游應用的開發者抱怨,爲什麼上游服務器已經正常了,用戶刷新頁面看到的仍是舊數據。做爲解決辦法,咱們能夠在 Nginx 的 header filter 階段,經過 Lua 代碼或者 Nginx C module,把 Cache-Control: max-age=...
修改爲 Cache-Control: no-cache
。這麼一來,Client 會在使用緩存以前先驗證下,若是 Nginx 返回 304 狀態碼,那麼該緩存會被繼續使用;若是上游已經 OK 了且更新了響應,那麼 Client 就會從新請求,避免使用過時的內容。緩存
這裏須要強調下,no-cache
並不是如字面上的意義表示不緩存,而是要求 Client 在使用該緩存以前,須要先驗證下被緩存的內容是否仍是最新的。MDN 的說法是:服務器
Forces caches to submit the request to the origin server for validation before releasing a cached copy.
對應的,RFC 7234 的說法:code
The "no-cache" request directive indicates that a cache MUST NOT use
a stored response to satisfy the request without successful
validation on the origin server.
若是要想讓 Client 不緩存響應的內容,按 MDN 上的說法,須要用 Cache-Control: no-cache, no-store, must-revalidate
(https://developer.mozilla.org...)。server
仔細看了下 no-cache
/ no-store
/ must-revalidate
這三項指令的介紹,彷佛 no-store
就能讓 Client 不用這個緩存,由於 no-store
要求:開發
The cache should not store anything about the client request or server response.
另外 must-revalidate
要求在使用過時緩存前驗證下該內容是不是最新的,而 no-cache
也是要求從新驗證的,那爲何須要兩個都一塊兒用呢?get
Google 搜索把我帶到了這個 SO 問答:https://stackoverflow.com/que...。這個回答裏面解釋了爲什麼不僅僅用 no-store
:由於臭名昭著的 IE6 瀏覽器在處理 no-store
時有 bug。但惋惜的是,這個回答沒有給出這一論斷的證據,好比 IE 的 bug report 之類。MDN 在給出 Cache-Control: no-cache, no-store, must-revalidate
這個例子的時候,也沒有說起更多的上下文。這很像沒有任何註釋的老代碼:咱們不知道當初爲什麼這麼寫,而把它刪掉彷佛不會帶來什麼問題。it