博客 / 詳情

返回

[瀏覽器] Expires,max-age,判斷緩存過期的瀏覽器實現

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

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.