【流程】
當資源第一次被訪問的時候,HTTP頭部如下
(Request-Line) GET /a.html HTTP/1.1
Host 127.0.0.1
User-Agent Mozilla/5.0 (X11; U; Linux i686;zh-CN;rv:1.9.0.15) Gecko/2009102815 Ubuntu/9.04 (jaunty) Firefox/3.0.15
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language zh-cn,zh;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset gb2312,utf-8;q=0.7,;q=0.7
Keep-Alive 300
Connection keep-alive
HTTP返回頭部如下
(Status-Line) HTTP/1.1 200 OK
Date Thu, 26 Nov 2009 13:50:54 GMT
Server Apache/2.2.11 (Unix) PHP/5.2.9
Last-Modified Thu, 26Nov 2009 13:50:19 GMT
Etag "8fb8b-14-4794674acdcc0"
Accept-Ranges bytes
Content-Length 20
Keep-Alive timeout=5, max=100
Connection Keep-Alive
Content-Type text/html
當資源第一次被訪問的時候,http返回200的狀態碼,並在頭部攜帶上當前資源的一些描述信息,如
Last-Modified // 指示最后修改的時間
Etag // 指示資源的狀態唯一標識
Expires // 指示資源在瀏覽器緩存中的過期時間
接着瀏覽器會將文件緩存到Cache目錄下,並同時保存文件的上述信息
當第二次請求該文件時,瀏覽器會先檢查Cache目錄下是否含有該文件,如果有,並且還沒到Expires設置的時間,即文件還沒有過期,那么此時瀏覽器將直接從Cache目錄中讀取文件,而不再發送請求
如果文件此時已經過期,則瀏覽器會發送一次HTTP請求到WebServer,並在頭部攜帶上當前文件的如下信息
If-Modified-Since Thu, 26 Nov 2009 13:50:19GMT
If-None-Match "8fb8b-14-4794674acdcc0"
即把上一次修改的時間,以及上一次請求返回的Etag值一起發送給服務器。服務器在接收到這個請求的時候,先解析Header里頭的信息,然后校驗該頭部信息。
如果該文件從上次時間到現在都沒有過修改或者Etag信息沒有變化,則服務端將直接返回一個304的狀態,而不再返回文件資源,狀態頭部如下
(Status-Line) HTTP/1.1 304 Not Modified
Date Thu, 26 Nov 2009 14:09:07 GMT
Server Apache/2.2.11 (Unix) PHP/5.2.9
Connection Keep-Alive
Keep-Alive timeout=5, max=100
Etag "8fb8b-14-4794674acdcc0"
這樣,就能夠很大程度上減少網絡帶寬以及提升用戶的瀏覽器體驗。
當然,如果服務器經過匹配發現文件修改過了,就會將文件資源返回,並帶上新文件狀態信息。
【基本字段】
Pragma
Pragma頭域用來包含實現特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1協議中,它的含義和Cache- Control:no-cache相同。
Expires
文件在本地緩存的過期時間,如果瀏覽器發現緩存中的文件沒有過期,則不發送請求(有例外,后面介紹)
Cache-Control
Cache -Control指定請求和響應遵循的緩存機制。
在請求消息或響應消息中設置 Cache-Control並不會修改另一個消息處理過程中的緩存處理過程。請求時的緩存指令包括
no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached
響應消息中的指令包括
public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age
各個消息中的指令含義如下:
1. Public指示響應可被任何緩存區緩存。
2. Private指示對於單個用戶的整個或部分響應消息,不能被共享緩存處理。這允許服務器僅僅描述當用戶的部分響應消息,此響應消息對於其他用戶的請求無效。
3. no-cache指示請求或響應消息不能緩存
4. no-store用於防止重要的信息被無意的發布。在請求消息中發送將使得請求和響應消息都不使用緩存。
5. max-age指示客戶機可以接收生存期不大於指定時間(以秒為單位)的響應。
6. min-fresh指示客戶機可以接收響應時間小於當前時間加上指定時間的響應。
7. max-stale指示客戶機可以接收超出超時期間的響應消息。如果指定max-stale消息的值,那么客戶機可以接收超出超時期指定值之內的響應消息。
Etag/If-None-Match
一對驗證文件實體的標記 “Entity Tag”的響應/請求頭Apache中,ETag的值,默認是對文件的索引節(INode),大小(Size)和最后修改時間(MTime)進行Hash后得到的
Last-Modified/If-Modified-Since
一對驗證文件的修改時間的響應/請求頭
Expires、Cache-Control、Last-Modified、ETag是RFC2616(HTTP/1.1)協議中和網頁緩存相關的幾個字段。
前兩個用來控制緩存的失效日期,瀏覽器可通過它來判定,需不需要發出HTTP請求;
后兩個用來驗證網頁的有效性,服務器端利用它來驗證這個文件是否需要重新返回
Last-ModifiedVS Etag
既然有了Last-Modified,為什么還要用ETag字段呢?因為如果在一秒鍾之內對一個文件進行兩次更改,Last-Modified就會不正確。因此,HTTP/1.1利用Entity Tag頭提供了更加嚴格的驗證。
【不同的情況】
上面描述的是一個普通的瀏覽器緩存狀態,在實際應用中,如頁面跳轉(點擊頁面鏈接跳轉,window.open,在地址欄敲回車,刷新頁面)等操作,會有一些區別
普通頁面跳轉
普通頁面跳轉包括鏈接點擊跳轉,用js腳本打開新頁面(window.open)
無緩存情況下,請求會返回所有資源結果
設置Expires並且未過期時,瀏覽器將不會發出http請求
如果Expires過期,則會發送相應請求,並附帶上Last-Modifed等信息,供服務器校驗
頁面刷新(F5)
這種情況一下,一般會看到很多304的請求,就是說即便資源設置了Expires且未過期,瀏覽器也會發送相應請求
IE和FF稍有區別
IE:
If-Modified-Since Wed, 18 Nov 2009 15:54:52GMT
If-None-Match "2360492659"
Pragma: no-cache // 禁止緩存
FF:
If-Modified-Since Wed, 18 Nov 2009 15:54:52GMT
If-None-Match "2360492659"
Cache-Control max-age=0 // 文件立即過期
強制刷新(Ctrl+F5)
效果和無緩存時候一致,返回200的結果
一些特殊的資源
IFRAME
我有一個主頁面包含iframe框架,iframe加載一個ajax操作json的頁面。當第一次打開這個主頁面的時候,iframe中頁面讀取json數據是最新的,當數據庫中修改了數據再刷新主頁面的時候,iframe中頁面的數據沒有從數據庫中取得最新的數據。這類問題,在FF中的解決方法可以參考How to get iframe not to cache in IE這篇文章,即在頁面的head部分加上以下語句:
<META http-equiv="Expires" content="Mon, 26 Jul 1997 05:00:00 GMT"> <META http-equiv="Last-Modified" content="Sat, 10 Nov 1997 09:08:07 GMT"> <META http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate"> <META http-equiv="Pragma" content="no-cache">
但在IE中無效。IE中解決iFrame緩存問題的方法有兩種:
(1) 每次主頁面刷新時隨機更換iframe的name;
(2) 每次主頁面刷新時在iframe的src路徑頁面賦予一個隨機get參數,例如:
<iframe src="http://www.example.com/thepage.html" name="aframe"></iframe> <script type="text/JavaScript"> document.frames['aframe'].location.href += (document.frames['aframe'].location.href.indexOf("?") != -1 ? "?" : "&") + (new Date()).getTime(); </script>
FLASH
使用以下的方法,使SWF文件強制不從瀏覽器讀本地的緩存。或強制其SWF文件每次都去 讀取最新的媒體文件
確保每次都讀取最新的SWF文件。
1:使用"Expires"標頭 這是在HTML文件中告訴瀏覽器不讀取本地緩存 在<head> </head> 中間加以下代碼 <!-- BEGIN INSERT --> <META HTTP-EQUIV="Expires" CONTENT="Mon, 04 Dec 1999 21:29:02 GMT"> <!-- END INSERT --> 這樣的話,每次訪問這個文件都會告訴瀏覽器其緩存版本過期,將重新從服務器端讀取最新的文件
2:直接告訴瀏覽器根本就沒有緩存 在包含SWF文件的HTML頁面里的</body>插入: <!-- BEGIN INSERT -->
<HEAD> <META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE"> </HEAD>
<!-- END INSERT --> 沒有Cache標頭 不支持IE5版本,所以微軟建議使用帶Cacahe控制標頭
3:當在HTML頁面間連接跳轉時 在點擊超連接時將強制其從服務器上下載最新文檔而不是從本地緩存中瀏覽
例如: <A HREF="stockPrices.htm?1">Current stock prices</A>
以上方法將阻止讀取本地緩存
如何阻止從緩存中讀取加載變量
問題: 當從外部數據源加載數據時,有時瀏覽器將數據存貯在本地緩存中,這樣就導致 在調用loadVariables方法加載數據時會從本地緩存中讀取數據而代替從原始數據 讀取的信息。
解決: 為確保flash加載的是最新的變量,附加一個隨機數變量,這樣就可以原始檔中加載最新的數據
例如: 方法一: loadVariables("mypage.asp?nocache=" + random(65000), 0, "POST");
方法二: loadVariables("mypage.asp?nocache=" + getTimer(), 0, "POST");
這樣確保每次加載的數據是最新的.
異步獲取的數據(AJAX)
項目有時要用一些Ajax的效果,因為比較簡單,也就沒有去用什么Ajax.NET之類的東西,手寫代碼也就實現了。、
第二天,有人向我報告錯誤;說是只有第一次讀取的值正常,后面的值都不正常;我調試了一下 ,確實有這樣的問題,查出是因為AJAX緩存的問題:解決辦法有如下幾種:
1、在服務端加 header("Cache-Control: no-cache, must-revalidate");(如php中)
2、在ajax發送請求前加上 anyAjaxObj.setRequestHeader("If-Modified-Since","0");
3、在ajax發送請求前加上 anyAjaxObj.setRequestHeader("Cache-Control","no-cache");
4、在 Ajax 的 URL 參數后加上 "?fresh=" + Math.random(); //當然這里參數 fresh 可以任意取了
5、第五種方法和第四種類似,在 URL 參數后加上 "?timestamp=" + new Date().getTime();
6、用POST替代GET:不推薦
1、加個隨機數 xmlHttp.open("GET", "ajax.asp?now=" + new Date().getTime(), true);
2、在要異步獲取的asp頁面中寫一段禁止緩存的代碼: Response.Buffer =True Response.ExpiresAbsolute =Now() - 1 Response.Expires=0 Response.CacheControl="no-cache"
3、在ajax發送請求前加上xmlHTTP.setRequestHeader("If-Modified-Since","0");可以禁止緩存 xmlHTTP.open("get", URL, true); xmlHTTP.onreadystatechange = callHTML; xmlHTTP.setRequestHeader("If-Modified-Since","0"); xmlHTTP.send();
另一個作者寫到:
AJAX的緩存是由瀏覽器維持的,對於發向服務器的某個url,ajax僅在第一次請求時與服務器交互信息,之后的請求中,ajax不再向服務器提交請求,而是直接從緩存中提取數據。 有些情況下,我們需要每一次都從服務器得到更新后數據。思路是讓每次請求的url都不同,而又不影響正常應用:在url之后加入隨機內容。 e.g. url=url+"&"+Math.random();
Key points: 1.每次請求的url都不一樣(ajax的緩存便不起作用) 2.不影響正常應用(最基本的)
---------------- 方法二:(未經證實) 在JSP中禁止緩存 response.addHeader("Cache-Control", "no-cache"); response.addHeader("Expires", "Thu, 01 Jan 1970 00:00:01 GMT");
HTTP: <META HTTP-EQUIV="pragma" CONTENT="no-cache"> <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate"> <META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT"> <META HTTP-EQUIV="expires" CONTENT="0">
另一個作者寫到:
我們都知道,ajax能提高頁面載入的速度的主要原因是通過ajax減少了重復數據的載入,真正做到按需獲取,既然如此,我們在寫ajax程序的時候不妨送佛送到西,在客戶端再做一次緩存,進一步提高數據載入速度。那就是在載入數據的同時將數據緩存在瀏覽器內存中,一旦數據被載入,只要頁面未刷新,該數據就永遠的緩存在內存中,當用戶再次查看該數據時,則不需要從服務器上去獲取數據,極大的降低了服務器的負載和提高了用戶的體驗。
【三種刷新方式對緩存的區別】
眾所周知瀏覽器是通過Last-Modified和Expires來處理緩存的,具體機制就不做解釋,而在具體調試中發現並不按我們想象的方式進行,其原因很有可能是在刷新瀏覽器的時候采用不恰當的方式導致。對於大多數瀏覽器而言,都包含有三種刷新方式,以下我們以IE瀏覽器為例:
F5 刷新 Ctrl+F5 刷新 “轉至”或地址欄里回車 刷新
這些快捷鍵的功能,主流瀏覽器都是相同的。 而這三種刷新方式會導致瀏覽器采取不同的緩存機制:
F5:不允許瀏覽器直接使用本地緩存,因此Last-Modified能起作用,但Expires無效 Ctrl+F5:是強制刷新,因此緩存機制失效 “轉至”或地址欄里回車:正常的訪問,Last-Modified和Expires都有效
知道瀏覽器刷新還有這么一回事后,那么以后在項目調試的過程中就不會感到疑惑了。