Cache-Control
Cache-Control 是最重要的規則。這個欄位用於指定所有緩衝機制在整個請求/響應鏈中必須服從的指令。這些指令指定用於阻止緩衝對請求或響應造成不利幹擾的行為。這些指令通常覆蓋預設緩衝演算法。緩衝指令是單向的,即請求中存在一個指令並不意味著響應中將存在同一個指令。
cache-control 定義是:Cache-Control = “Cache-Control” “:” cache-directive。表 1 展示了適用的值。
表 1. 常用 cache-directive 值
| Cache-directive |
說明 |
| public |
所有內容都將被緩衝 |
| private |
內容只緩衝到私人緩衝中 |
| no-cache |
所有內容都不會被緩衝 |
| no-store |
所有內容都不會被緩衝到緩衝或 Internet 臨時檔案中 |
| must-revalidation/proxy-revalidation |
如果緩衝的內容失效,請求必鬚髮送到伺服器/代理以進行重新驗證 |
| max-age=xxx (xxx is numeric) |
緩衝的內容將在 xxx 秒後失效, 這個選項只在HTTP 1.1可用, 並如果和Last-Modified一起使用時, 優先順序較高 |
在用戶端通過瀏覽器發出第一次請求某一個URL時,根據 HTTP 協議的規定,瀏覽器會向伺服器傳送前序(Http Request Header),伺服器端響應同時記錄相關屬性標記(Http Reponse Header),伺服器端的返回狀態會是200,格式類似如下:
| 代碼如下 |
複製代碼 |
HTTP/1.1 200 OK Date: Tue, 03 Mar 2009 04:58:40 GMT Content-Type: image/jpeg Content-Length: 83185 Last-Modified: Tue, 24 Feb 2009 08:01:04 GMT Cache-Control: max-age=2592000 Expires: Thu, 02 Apr 2009 05:14:08 GMT Etag: “5d8c72a5edda8d6a:3239″ |
用戶端第二次請求此URL時,根據 HTTP 協議的規定,瀏覽器會向伺服器傳送前序(Http Request Header),伺服器端響應並記錄相關記錄屬性標記檔案沒有發生改動,伺服器端返回304,直接從緩衝中讀取:
| 代碼如下 |
複製代碼 |
HTTP/1.x 304 Not Modified Date: Tue, 03 Mar 2009 05:03:56 GMT Content-Type: image/jpeg Content-Length: 83185 Last-Modified: Tue, 24 Feb 2009 08:01:04 GMT Cache-Control: max-age=2592000 Expires: Thu, 02 Apr 2009 05:14:08 GMT Etag: “5d8c72a5edda8d6a:3239″ |
一、Last-Modified、Expires和Etag相關工作原理
1、Last-Modified
在瀏覽器第一次請求某一個URL時,伺服器端的返回狀態會是200,內容是你請求的資源,同時有一個Last-Modified的屬性標記(Http Reponse Header)此檔案在服務期端最後被修改的時間,格式類似這樣:
1 Last-Modified: Tue, 24 Feb 2009 08:01:04 GMT
用戶端第二次請求此URL時,根據 HTTP 協議的規定,瀏覽器會向伺服器傳送 If-Modified-Since 前序(Http Request Header),詢問該時間之後檔案是否有被修改過:
1 If-Modified-Since: Tue, 24 Feb 2009 08:01:04 GMT
如 果伺服器端的資源沒有變化,則自動返回 HTTP 304 (Not Changed.)狀態代碼,內容為空白,這樣就節省了傳輸資料量。當伺服器端代碼發生改變或者重啟伺服器時,則重新發出資源,返回和第一次請求時類似。從而 保證不向用戶端重複發出資源,也保證當伺服器有變化時,用戶端能夠得到最新的資源。
註:如果If-Modified-Since的時間比伺服器目前時間(當前的請求時間request_time)還晚,會認為是個非法請求
2、Etag工作原理
HTTP 協議規格說明定義ETag為“被請求變數的實體標記” (參見14.19)。
簡單點即伺服器響應時給請求URL標記,並在HTTP回應標頭中將其傳送到用戶端,類似伺服器端返回的格式:
1 Etag: “5d8c72a5edda8d6a:3239″
用戶端的查詢更新格式是這樣的:
1 If-None-Match: “5d8c72a5edda8d6a:3239″
如果ETag沒改變,則返回狀態304。
即:在用戶端發出請求後,Http Reponse Header中包含 Etag: “5d8c72a5edda8d6a:3239″
標識,等於告訴Client端,你拿到的這個的資源有表示ID:5d8c72a5edda8d6a:3239。
當下次需要發Request索要同一個URI的時候,瀏覽器同時發出一個If-None-Match前序( Http Request Header)此時包頭中資訊包含上次訪問得到的Etag: “5d8c72a5edda8d6a:3239″標識。
1 If-None-Match: “5d8c72a5edda8d6a:3239″
這樣,Client端等於Cache了兩份,伺服器端就會比對2者的etag。如果If-None-Match為False,不返回200,返回304 (Not Modified) Response。
3、Expires
給出的日期/時間後,被響應認為是過時。如Expires: Thu, 02 Apr 2009 05:14:08 GMT
需和Last-Modified結合使用。用於控制請求檔案的有效時間,當請求資料在有效期間內時用戶端瀏覽器從緩衝請求資料而不是伺服器端. 當緩衝中資料失效或到期,才決定從伺服器更新資料。
4、Last-Modified和Expires
Last-Modified標識能夠節省一點頻寬,但是還是逃不掉發一個HTTP請求出去,而且要和Expires一起用。而Expires標識卻使得瀏 覽器乾脆連HTTP請求都不用發,比如當使用者F5或者點擊Refresh按鈕的時候就算對於有Expires的URI,一樣也會發一個HTTP請求出去, 所以,Last-Modified還是要用的,而 且要和Expires一起用。
5、Etag和Expires
如果伺服器端同時設定了Etag和Expires時,Etag原理同樣,即與Last-Modified/Etag對應的Http Request Header:If-Modified-Since和If-None-Match。我們可以看到這兩個Header的值和Web Server發出的Last-Modified,Etag值完全一樣;在完全符合If-Modified-Since和If-None-Match即檢查 完修改時間和Etag之後,伺服器才能返回304.
6、Last-Modified和Etag
Last-Modified 和ETags請求的http前序一起使用,伺服器首先產生 Last-Modified/Etag標記,伺服器可在稍後使用它來判斷頁面是否已經被修改,來決定檔案是否繼續緩衝
過程如下:
1. 用戶端請求一個頁面(A)。
2. 伺服器返回頁面A,並在給A加上一個Last-Modified/ETag。
3. 用戶端展現該頁面,並將頁面連同Last-Modified/ETag一起緩衝。
4. 客戶再次請求頁面A,並將上次請求時伺服器返回的Last-Modified/ETag一起傳遞給伺服器。
5. 伺服器檢查該Last-Modified或ETag,並判斷出該頁面自上次用戶端請求之後還未被修改,直接返迴響應304和一個空的響應體。
註:
1、Last-Modified和Etag頭都是由Web Server發出的Http Reponse Header,Web Server應該同時支援這兩種頭。
2、Web Server發送完Last-Modified/Etag頭給用戶端後,用戶端會緩衝這些頭;
3、用戶端再次發起相同頁面的請求時,將分別發送與Last-Modified/Etag對應的Http Request Header:If-Modified-Since和If-None-Match。我們可以看到這兩個Header的值和Web Server發出的Last-Modified,Etag值完全一樣;
4、通過上述值到伺服器端檢查,判斷檔案是否繼續緩衝;
二、對於非即時互動動態網頁面中Epires和Etag處理
對資料更新並不頻繁、如tag分類歸檔等等,可以考慮對其cache。簡單點就是在非即時互動的動態程式中輸出expires和etag標識,讓其緩衝。 但需要注意關閉session,防止http response時http header包含session id標識;
3.1、Expires
如expires.php
| 代碼如下 |
複製代碼 |
header(’Cache-Control: max-age=86400,must-revalidate’); header(’Last-Modified: ‘ .gmdate(’D, d M Y H:i:s’) . ‘ GMT’ ); header(”Expires: ” .gmdate (’D, d M Y H:i:s’, time() + ‘86400′ ). ‘ GMT’); |
3.2、Etag
根據Http返回狀態來處理。當返回304直接從緩衝中讀取
如etag.php
| 代碼如下 |
複製代碼 |
cache(); echo date(”Y-m-d H:i:s”); function cache() { $etag = “http://longrujun.name”; if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) { header(’Etag:’.$etag,true,304); exit; } else header(’Etag:’.$etag); } |
圖片緩衝執行個體
| 代碼如下 |
複製代碼 |
$imagePath = "path/to/some/image"; $eTag = $imagePath; $eTag .= fileMTime($imagePath); $eTag = md5($eTag); if((isset($_SERVER['HTTP_IF_NONE_MATCH'])) && (stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) == $eTag)) { header("HTTP/1.1 304 Not Modified", TRUE, 304); exit(); } header("ETag: ".$eTag); header("Content-Type: image/png"); readFile($imagePath); exit();
|
上面的方法超級簡單,下面看個完整執行個體
| 代碼如下 |
複製代碼 |
$fullpath = '/www/images/' . basename($_GET['img']); //假定檔案都在/www/images/下 if (!is_file($fullpath)) { header("HTTP/1.0 404 Not Found"); exit(); } $info = getImageSize($fullpath); //擷取圖片資訊 if (!$info) { //如果不是圖片 header("HTTP/1.0 404 Not Found"); exit(); } // 以下凡是header函數都是在輸出頭部資訊。較多。 header('Content-type: '. $info['mime']); //類似於image/png header('Content-Length: '. filesize($fullpath)); //檔案長度 header('Pragma: '); //沒用,但要設定,防止伺服器產生no-cache的可怕字眼 //手動設定到期時間,單位都是秒 $validtime = 48* 60 * 60; // 48小時 //緩衝相對請求的時間, header('Cache-Control: ' . 'max-age='. $validtime); //也很重要的Expires頭,功能類似於max-age //time()+$validtime: 設定期限,到期後才會向伺服器提交請求 //gmdate,產生Sun, 01 Mar 2009 04:05:49 +0000 的字串,而且是GMT標準時區 //preg_replace, 產生Sun, 01 Mar 2009 04:05:49 GMT, 注意:可能與伺服器設定有關, //但我都用預設設定 header('Expires:'. preg_replace('/.{5}$/', 'GMT', gmdate('r', time()+ $validtime))); //檔案最後修改時間 $lasttime = filemtime($fullpath); //最後修改時間,設定了,點擊重新整理時,瀏覽器再次請求圖片才會發出'IF_MODIFIED_SINCE'頭, //從而被php程式讀取 header('Last-Modified: ' . preg_replace('/.{5}$/', 'GMT', gmdate('r', $lasttime) )); //重要,如果請求中的時間和 檔案產生時間戳記相等,則檔案未修改,用戶端可用緩衝 if (strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lasttime) { header("HTTP/1.1 304 Not Modified"); //伺服器發出檔案不曾修改的指令 exit(); } //如果檔案被修改了,只好重新發出資料 echo file_get_contents($fullpath); |