1. 程式人生 > >淺談http中的Cache-Control

淺談http中的Cache-Control

前言

我們用http訪問時,會先發送一個請求,之後伺服器返回一個應答,在Chrome的偵錯程式(按F12或右擊選擇檢查)中展現了整個過程: 第一部分General是概要,包含請求地址,請求方式,狀態碼,伺服器地址以及Referrer 策略。 第二部分是應答頭部,是伺服器返回的。 第三部分是請求頭部,是客戶端傳送的。

在http中,控制快取開關的欄位有兩個:Pragma 和 Cache-Control。

Pragma

Pragma有兩個欄位Pragma和Expires。Pragma的值為no-cache時,表示禁用快取,Expires的值是一個GMT時間,表示該快取的有效時間。 Pragma是舊產物,已經逐步拋棄,有些網站為了向下相容還保留了這兩個欄位。如果一個報文中同時出現Pragma和Cache-Control時,以Pragma為準。同時出現Cache-Control和Expires時,以Cache-Control為準。即優先順序從高到低是 Pragma -> Cache-Control -> Expires

Cache-Control

在介紹之前,先囉嗦兩個容易忽視的地方:

  1. 符合快取策略時,伺服器不會發送新的資源,但不是說客戶端和伺服器就沒有會話了,客戶端還是會發請求到伺服器的。
  2. Cache-Control除了在響應中使用,在請求中也可以使用。我們用偵錯程式來模擬下請求時帶上Cache-Control:勾選Disable cache,重新整理頁面,可以看到Request Headers中有個欄位Cache-Control: no-cache。 同時在Response Headers中也能到Cache-Control欄位,它的值是must-revalidate,這是服務端設定的。

Cache-Control欄位

在請求中使用Cache-Control 時,它可選的值有: 在響應中使用Cache-Control 時,它可選的值有: 在這裡插入圖片描述 no-store優先順序最高 在Cache-Control 中,這些值可以自由組合,多個值如果衝突時,也是有優先順序的,而no-store優先順序最高。我們可以測試下:在nginx中做如下配置:

server {
    listen 88;
    root /opt/ms;
    index index.php index.html index.htm index.nginx-debian.html;
    
    location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
        add_header Cache-Control no-store;
        add_header Cache-Control max-age=3600;
        add_header Cache-Control public;
        add_header Cache-Control only-if-cached;
        add_header Cache-Control no-cache;
        add_header Cache-Control must-revalidate;
    }
}

在/opt/ms下增加個檔案type.css,內容如下:

a{
    color: #000000;
}
a:focus,a:hover {
    text-decoration: none;
    color: #708090;
}

配置好之後,reload下nginx,在瀏覽器訪問地址http://127.0.0.1:88/type.css,可以看到響應頭部包含nginx配置中的欄位: 重複重新整理訪問,會發現每次的狀態碼都是200,原因是no-store的優先順序最高,本地不儲存,每次都需要伺服器傳送資源。

public和private的選擇 如果你用了CDN,你需要關注下這個值。CDN廠商一般會要求cache-control的值為public,提升快取命中率。如果你的快取命中率很低,而訪問量很大的話,可以看下是不是設定了private,no-cache這類的值。如果定義了max-age,可以不用再定義public,它們的意義是一樣的。

哪裡設定Cache-Control

以LNMP的環境為例,一次響應經歷的過程是:php-cgi解析程式碼並執行,將結果返回給nginx,如果nginx前面有反向代理,則會經過一次反向代理伺服器,所以cache-control可能會在nginx,php-cgi,php程式碼,反向代理伺服器這些地方地方設定。在php.ini中,有個引數是session.cache_limiter,需要注意下。在nginx中有個很常見的配置:

location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ { 
            expires      30d;
	}

這個指令等同於cache-control: max-age=2592000,同時你會在響應頭部看到一個etag欄位,這是由於nginx預設開啟,如果要關閉可以增加個配置etag off。這個etag就是我們接下要看的快取校驗欄位。

快取校驗欄位

在快取中,我們需要一個機制來驗證快取是否有效。比如伺服器的資源更新了,客戶端需要及時重新整理快取;又或者客戶端的資源過了有效期,但伺服器上的資源還是舊的,此時並不需要重新發送。快取校驗就是用來解決這些問題的,在http 1.1 中,我們主要關注下Last-Modified 和 etag 這兩個欄位。

Last-Modified

服務端在返回資源時,會將該資源的最後更改時間通過Last-Modified欄位返回給客戶端。客戶端下次請求時通過If-Modified-Since或者If-Unmodified-Since帶上Last-Modified,服務端檢查該時間是否與伺服器的最後修改時間一致:如果一致,則返回304狀態碼,不返回資源;如果不一致則返回200和修改後的資源,並帶上新的時間。 If-Modified-Since和If-Unmodified-Since的區別是: If-Modified-Since:告訴伺服器如果時間一致,返回狀態碼304 If-Unmodified-Since:告訴伺服器如果時間不一致,返回狀態碼412

etag

單純的以修改時間來判斷還是有缺陷,比如檔案的最後修改時間變了,但內容沒變。對於這樣的情況,我們可以使用etag來處理。 etag的方式是這樣:伺服器通過某個演算法對資源進行計算,取得一串值(類似於檔案的md5值),之後將該值通過etag返回給客戶端,客戶端下次請求時通過If-None-Match或If-Match帶上該值,伺服器對該值進行對比校驗:如果一致則不要返回資源。 If-None-Match和If-Match的區別是: If-None-Match:告訴伺服器如果一致,返回狀態碼304,不一致則返回資源 If-Unmodified-Since:告訴伺服器如果不一致,返回狀態碼412

總結

  1. 快取開關是: pragma, cache-control。
  2. 快取校驗有:Expires,Last-Modified,etag。
  3. 從狀態碼的角度來看,它們的關係如下圖:
  4. cache-control的各個值關係如下圖

參考資料