1. 程式人生 > >HTTP請求中瀏覽器的快取機制

HTTP請求中瀏覽器的快取機制

【流程】

當資源第一次被訪問的時候,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">

(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都有效

知道瀏覽器重新整理還有這麼一回事後,那麼以後在專案除錯的過程中就不會感到疑惑了。