Last-Modifed、Expires以及ETag、Cache-Control的動作流程與關係

在我們用伺服器端的動態頁面,去打出一些靜態的資料時,我們總希望不要讓伺服器那麼累,想要設法讓瀏覽器不要一直來跟伺服器端要一些不太可能會變動的資料,這時候我們就得需要瀏覽器的快取機制。瀏覽器快取機制(Browser Cache)自HTTP 1.0以來存在已久,但是許多前後端的程式設計師卻沒有對其有太大的重視,以我自己來說,最多也是實作到Last-Modifed而已,這也不是說不好,只是在某一些狀況下,還是要浪費一次的GET通訊,對於吞吐量大的網站來說,是一種效能上的負擔。

在說明前要先請大家了解,基本上Last-Modifed與Expires互成一對,ETag與Cache-Control互成一對。

Last-Modified動作

  1. Client端跟Server端要一個資源,Server回應HTTP 200 OK,並打下一個Last-Modified日期。
  2. 當客戶端再次要求同一個資源時,會先去檢查自己的Cache裡面有沒有相同的資料,有的話,會檢查是不是過期了(Expires or Cache-Control)。
  3. 沒有過期的話:取出自己的資源,停止對伺服器發送請求,流程結束。
  4. 如果已經過期,或者是根本就找不到與過期有關的標籤,那麼就對伺服器丟出If-Modified-Since標頭,標頭後面的日期當然是複製之前被伺服器端指定的日期了。
  5. 伺服器端收到If-Modified-Since的日期,就可以拿來判斷是否要重新丟或者拒絕丟新的資源,如果拒絕丟的話,就拋出HTTP 304 Not Modified,如果重新丟的話,就丟吧。
  6. Client端如果收到HTTP 304,就會乖乖的把Cache拿出來繼續用。

ETag動作

  1. Client端跟Server端要一個資源,Server回應HTTP 200 OK,並打下一個ETag雜湊。(雜湊的產生方式由程式設計師自己制定,這邊沒有太多的規範。)
  2. 當客戶端再次要求同一個資源時,會先去檢查自己的Cache裡面有沒有相同的資料,有的話,會檢查是不是過期了(Expires or Cache-Control)。
  3. 沒有過期的話:取出自己的資源,停止對伺服器發送請求,流程結束。
  4. 如果已經過期,或者是根本就找不到與過期有關的標籤,那麼就對伺服器丟出If-None-Match標頭,標頭後面的雜湊當然是複製之前被伺服器端指定的雜湊了。
  5. 伺服器端收到If-None-Match的雜湊,就可以拿來判斷是否要重新丟或者拒絕丟新的資源,如果拒絕丟的話,就拋出HTTP 304 Not Modified,如果重新丟的話,就丟吧。
  6. Client端如果收到HTTP 304,就會乖乖的把Cache拿出來繼續用。

由上面可以得知,Last-Modified、ETag這兩種機制的運作模式幾乎是一樣的。

再論Expires與Cache-Control

Expires是HTTP 1.0的產物,而Cache-Control是HTTP 1.1的產物,這兩個都是在描述資源的過期時間點,但是為何要創造出兩種的標頭呢?原因在於Expires是從伺服器端打下來的時間,這一點是比較有爭議的,尤其當Client端的時間進行比較大的變動時(例如出國換時區、電腦的時間跳掉),整個快取的機制就毀掉了。因此後來Cache-Control把機制改成計算制度(秒數),也就是說拿出Client端時間來相加此秒數,就等於是過期時間點。※註:事實上仔細一想,如果你的Client時間真的錯誤了,那麼用Cache-Control依然無法解決問題。

這邊會在導論出一個問題點,那就是ETag是以雜湊為基底的,在取得不到日期的情況下,如何拿來與Cache-Control的秒數來相加,並計算出過期時間呢?這一點我找了許多的文章,目前還沒有一定的解釋。我推測有可能是拿標頭的Date或Last-Modifed來運算,也有可能瀏覽器自己制定了一個計時的功能,或者是拿本地端的檔案存檔時間來算,這都有可能。所以我的作法選怎就算你打出ETag,還是會再幫忙打出一次Last-Modifed來進行輔助,以利計算過期時間。因此Google這篇使用瀏覽器快取功能的文章中,有關於「同時指定Last-Modified和ETag,都是多餘的行為。」這句話值得玩味,如果知道的網友也麻煩留言指教一下。

此外值得一提的是,在現代的瀏覽器中,如果你設定了Cache-Control而沒有設定Expires,則Cache-Control會蓋過Expires的設定。另外也要注意的是,RFC2616規範,Cache的設定不可以超過一年。

總結

如果你把這篇文章從頭看到尾,你就會發現使用Last-Modifed與Cache-Control這兩個標頭就好,其他都有如浮雲般的無意義,這個再次的證明,革新不代表一定就是好事。當然啦,也不能說ETag就沒有存在的意義,想想看,如果你的伺服器群共用某一個資源(例如你公司的Logo圖示),但是基於某些因素你沒有辦法讓這些伺服器群的時間同步,那麼選用ETag的機制來對此圖示進行雜湊,會是一個比較好的做法。

補充

若你只有使用Last-Modifed而沒有使用Expires or Cache-Control的缺點是,在強制重新整理網頁時,你看不出任何效果。但是當你在同一個URL下,直接在網址列上再按下一次ENTER,你就會發現,沒有使用到Expires or Cache-Control的資源,會強制再去跟伺服器要一次GET,只不過會得到304,而這個通訊根本是非必要的。

HTTP200 HTTP304 Last-Modifed Expires ETag Cache-Control