Expires (http1.0)
在HTTP Header中通過Expires字段 傳遞一個GMT格式的字符串。
舉例:
Expires: Wed Feb 20 2019 11:25:41 GMT
Cache-Control: max-age=<seconds> (http1.1)
在HTTP Header中通過Cache-Control字段中的值max-age傳遞一個max-age=<seconds>的字符串。
舉例:
Cache-Control: max-age=3600
如果Expires和Cache-Control: max-age=<seconds>二者同時存在,max-age 的優先級高於 expires
(瀏覽器會根據 max-age 指令的值來決定緩存的有效期,而忽略 Expires)
上面這段話是在中文互聯網上出現的很多的內容,表述可能不一樣,但是內容就是這些了。
那麼瀏覽器到底怎麼判斷緩存過期?用max-age判斷緩存過期是否與本地時間有關?
在中文互聯網的論壇中,基本都是説Expires字段設置的緩存過期時間會受本地時間影響,max-age設置的緩存過期時間不會受本地時間影響。
-那麼在使用max-age設置緩存後,瀏覽器到底怎麼判斷緩存過期?判斷緩存過期是否與本地時間有關?
看我下面這個問題場景。
如果HTTP響應中包含以下頭部:
Date: Wed, 16 Oct 2019 07:42:37 GMT
Cache-Control: max-age=3600
按照中文互聯網上的常見表述
"瀏覽器記錄下max-age的時間點是通過在HTTP響應中的Date頭部來完成的。Date頭部指示了服務器響應請求的時間,它通常以格林威治標準時間 (GMT) 格式表示。"
那麼瀏覽器會計算出資源的有效期截止時間為Wed, 16 Oct 2019 08:42:37 GMT,(即當前時間加上max-age指定的秒數) 。瀏覽器會記錄這個時間點,以便後續請求時判斷資源是否過期。
問題:
當瀏覽器第二次發請求的時候,瀏覽器是否需要找到一個時間節點T2 來判定T2與 Wed, 16 Oct 2019 08:42:37 GMT的大小關係?
-如果需要T2,那麼T2如何取值? (是否受本地時間的影響?)
-如果不需要T2,那麼瀏覽器通過什麼來判定這個緩存是否過期?
下面開始進入正題,查證的過程中我發現有不少(錯誤的)內容在中文互聯網上被互相轉載,站站相傳,屬實離譜。
本文引用材料均有出處。各位讀者可以點link查看。
以chromium內核的實現為例來説明
1、判斷緩存是否過期
https://github.com/chromium/chromium/blob/main/net/http/http_response_headers.cc#L1107-L1114
公式如下:
response_is_fresh = (freshness_lifetime > current_age)
這裏有個字段 response_is_fresh (相應依舊新鮮)
字面理解,如果response_is_fresh為true,則表示緩存未過期。
如果保鮮時間(freshness_lifetime) 大於 當前經歷時間(current_age),則表示緩存未過期。
再往下看,找到freshness_lifetime和current_age的計算方法。
2、freshness_lifetime的計算 ( 保鮮時間(有效期)的計算 )
https://github.com/chromium/chromium/blob/main/net/http/http_response_headers.cc#L1141-L1163
這段話裏明確説明max-age的優先級大於expires
如果有max-age
freshness_lifetime = max_age_value
如果沒有max-age
freshness_lifetime = expires_value - date_value
看1152 1153兩行
Note that neither of these calculations is vulnerable to clock skew, since all of the information comes from the origin server
"注意,這兩種計算方式都不會受到時鐘偏差的影響,因為所有信息都來自於源服務器。"
也就是説,不論是用max-age還是expires計算freshness_lifetime,都不會受到時鐘偏差的影響。
3、current_age的計算 ( 當前經歷時間的計算 )
https://github.com/chromium/chromium/blob/main/net/http/http_response_headers.cc#L1264-L1332
// From RFC 7234 section 4.2.3:
//
// The following data is used for the age calculation:
//
// age_value
//
// The term "age_value" denotes the value of the Age header field
// (Section 5.1), in a form appropriate for arithmetic operation; or
// 0, if not available.
//
// date_value
//
// The term "date_value" denotes the value of the Date header field,
// in a form appropriate for arithmetic operations. See Section
// 7.1.1.2 of [RFC7231] for the definition of the Date header field,
// and for requirements regarding responses without it.
//
// now
//
// The term "now" means "the current value of the clock at the host
// performing the calculation". A host ought to use NTP ([RFC5905])
// or some similar protocol to synchronize its clocks to Coordinated
// Universal Time.
//
// request_time
//
// The current value of the clock at the host at the time the request
// resulting in the stored response was made.
//
// response_time
//
// The current value of the clock at the host at the time the
// response was received.
//
// The age is then calculated as
//
// apparent_age = max(0, response_time - date_value);
// response_delay = response_time - request_time;
// corrected_age_value = age_value + response_delay;
// corrected_initial_age = max(apparent_age, corrected_age_value);
// resident_time = now - response_time;
// current_age = corrected_initial_age + resident_time;
1264行到1305行,這一段註釋裏的now,對應下面HttpResponseHeaders::GetCurrentAge這個方法中的current_time(L1328)
The term "now" means "the current value of the clock at the host performing the calculation". A host ought to use NTP ([RFC5905]) or some similar protocol to synchronize its clocks to Coordinated Universal Time.
關於now的註釋這裏寫明瞭
"now"一詞表示"執行計算的主機上時鐘的當前值"。主機應該使用NTP([RFC5905])或類似協議將其時間與世界時鐘同步。
那麼current_age的計算是需要依賴於客户端主機的本地時鐘的。
回到這個問題
如果HTTP響應中包含以下頭部:
Date: Wed, 16 Oct 2019 07:42:37 GMT
Cache-Control: max-age=3600
當瀏覽器第二次發請求的時候,瀏覽器是否需要找到一個時間節點T2來判定T2與 Wed, 16 Oct 2019 08:42:37 GMT的大小關係?
-如果需要T2,那麼T2如何取值? (是否受本地時間的影響?)
-如果不需要T2,那麼瀏覽器通過什麼來判定這個緩存是否過期?
總結下來:
(chromium內核的)瀏覽器用的是比較freshness_lifetime(約等於max-age,保鮮時間,是一個時間跨度)和current_age(當前經歷時間,也是一個時間跨度)來計算有效期。
不需要時間節點T2。
計算freshness_lifetime用的是服務器時間, 不受本地時間影響。
但計算current_age用到了本地時間(current_time, 也就是註釋中的now),而且沒法保證本地時間與服務器時間一致。
因此,不論是Expires還是Cache-Control: max-age=<seconds> 計算緩存有效期的時候,都會受到本地時間的影響。
同步更新到自己的語雀,昨天(2023-10-23)下午語雀崩了,笑哈哈。
https://www.yuque.com/dirackeeko/blog/qun7u06okedz4yck