Lase-Modified
Lase-Modified比较好理解,最后修改时间,后台可以直接修改, 他的语法是:
Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
<day-name>
"Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 或 "Sun" 之一 (区分大小写)。
<day>
两位数字表示的天数, 例如"04" or "23"。
<month>
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 之一(区分大小写)。
<year>
4位数字表示的年份, 例如 "1990" 或者"2016"。
<hour>
两位数字表示的小时数, 例如 "09" 或者 "23"。
<minute>
两位数字表示的分钟数,例如"04" 或者 "59"。
<second>
两位数字表示的秒数,例如 "04" 或者 "59"。
GMT
国际标准时间。HTTP中的时间均用国际标准时间表示,从来不使用当地时间。
ETag
ETagHTTP响应头是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽,因为如果内容没有改变,Web服务器不需要发送完整的响应。而如果内容发生了变化,使用ETag有助于防止资源的同时更新相互覆盖(“空中碰撞”)。
语法
ETag: W/"<etag_value>"
ETag: "<etag_value>"
W/ 可选
'W/'
(大小写敏感) 表示使用弱验证器。 弱验证器很容易生成,但不利于比较。 强验证器是比较的理想选择,但很难有效地生成。 相同资源的两个弱Etag
值可能语义等同,但不是每个字节都相同。
"<etag_value>"
实体标签唯一地表示所请求的资源。 它们是位于双引号之间的ASCII字符串(如“675af34563dc-tr34”)。 没有明确指定生成ETag值的方法。 通常,使用内容的散列,最后修改时间戳的哈希值,或简单地使用版本号。 例如,MDN使用wiki内容的十六进制数字的哈希值。
Etag, Last-Modified 与 Cache-Control的区别和使用
整个 Web 系统架构在 HTTP 协议 之上, 利用 HTTP 的缓存机制不仅可以极大地减少服务器负载, 更重要的是加速页面的载入,以及减少用户的流量消耗。 快速到达和易于访问是 Web 与生俱来的特性, 其缓存机制也早已被服务器和浏览器厂商广泛地实现, 我们作为 Web 内容的作者何乐而不为呢?
Web 服务器(比如 Tomcat、Apache、Virgo)或服务器端框架(比如 Django、Express.js) 都会实现 HTTP 缓存机制,但本文不借助这些框架, 而是直接以基本的 Node.js 程序与 Chrome 浏览器来描述 HTTP 中最基本的缓存机制, 涉及到的 HTTP 头字段 包括 Cache-Control
, Last-Modified
, If-Modified-Since
, Etag
, If-None-Match
等。
HTTP 缓存简介
谈起 HTTP 缓存你首先想到的一定是磁盘缓存,以及 304 状态码。 这是浏览器处理缓存的两种情况:
- 浏览器询问服务器缓存是否有效,服务器返回 304 指示浏览器使用缓存。
- 资源仍然处于有效期时,浏览器会直接使用磁盘缓存(在刷新时稍有不同,见下文)。
图中 favicon.ico
直接来自磁盘缓存,而 localhost
文档则来自 304 缓存。 上述行为中涉及到 3 个 HTTP 响应头字段:
-
Cache-Control
响应头表示了资源是否可以被缓存,以及缓存的有效期。 -
Etag
响应头标识了资源的版本,此后浏览器可据此进行缓存以及询问服务器。 -
Last-Modified
响应头标识了资源的修改时间,此后浏览器可据此进行缓存以及询问服务器。
Cache-Control
Cache-Control
在 HTTP 响应头中,用于指示代理和 UA 使用何种缓存策略(感谢 Kingsley Chen 指出此前对 no-cache 描述的错误)。比如:
- no-cache 为本次响应不可直接用于后续请求(在没有向服务器进行校验的情况下)
- no-store 为禁止缓存(不得存储到非易失性介质,如果有的话尽量移除,用于敏感信息)
-
private
为仅 UA 可缓存 -
public
为大家都可以缓存。
当Cache-Control
为可缓存时,同时可指定缓存时间(比如public, max-age:86400
)。 这意味着在 1 天(60x60x24=86400)时间内,浏览器都可以直接使用该缓存(此时服务器收不到任何请求)。 当然浏览器也有权随时丢弃任何一项缓存,因此这里可能有一致性问题。 注意下图中状态码附近的 from disk cache
标识。
[图片上传中...(image-7fa347-1550759661562-2)]
其服务器代码如下:
import http from 'http'
let server = http.createServer((req, res) => {
res.setHeader('Cache-Control', 'public, max-age=86400')
res.end('harttle.land')
})
server.listen(3333)
除了
Cache-Control
中的max-age
外,Expires
,Vary
等头字段也可用来设置缓存的有效性。
Etag
如果资源本身确实会随时发生改动,还用 Cache-Control
就会使用户看到的页面得不到更新。 但如果还希望利用 HTTP 缓存(万一资源没变呢),这就需要有条件的(conditional)HTTP 请求。
Etag
响应头字段表示资源的版本,浏览器在发送请求时会带 If-None-Match
头字段, 来询问服务器该版本是否仍然可用。如果服务器发现该版本仍然是最新的, 就可以返回 304 状态码指示 UA 继续使用缓存。注意下图中的 If-None-Match
字段。
[图片上传失败...(image-cecd6c-1550759661562)]
其服务器端代码如下:
import http from 'http'
let server = http.createServer((req, res) => {
console.log(req.url, req.headers['if-none-match'])
if (req.headers['if-none-match']) {
// 检查文件版本
res.statusCode = 304
res.end()
}
else {
res.setHeader('Etag', '00000000')
res.end('harttle.land')
}
})
server.listen(3333)
Last-Modified
与 Etag
类似,Last-Modified
HTTP 响应头也用来标识资源的有效性。 不同的是使用修改时间而不是实体标签。对应的请求头字段为If-Modified-Since
, 见下图:
其服务器端代码如下:
import http from 'http'
let server = http.createServer((req, res) => {
console.log(req.url, req.headers['if-modified-since'])
if (req.headers['if-modified-since']) {
// 检查时间戳
res.statusCode = 304
res.end()
}
else {
res.setHeader('Last-Modified', new Date().toString())
res.end('harttle.land')
}
})
server.listen(3333)
浏览器刷新
撰写这篇文章的过程中,Harttle 使用了很多 Chrome 浏览器的截图。 如果你使用浏览器调试,可能也需要了解刷新按钮的行为。
正常重新加载
按下刷新按钮或快捷键(在 MacOS 中是 Cmd+R)会触发浏览器的“正常重新加载”(normal reload), 此时浏览器会执行一次 Conditional GET。 Cache-Control
等缓存头字段会被忽略,并且带If-None-Match
, If-Modified-Since
等头字段。 此时服务器总会收到一次 HTTP GET 请求。 在 Chrome 中按下刷新,浏览器还会带如下请求头:
Cache-Control:max-age=0
注意:在地址栏重新输入当前页面地址并按下回车也会当做刷新处理, 这意味着只有从新标签页或超链接打开时,才能观察到直接使用硬盘缓存的情况。
强制重新加载
在 Chrome 中按下 Cmd+Shift+R (MacOS)可以触发强制重新加载(Hard Reload), 此时包括页面本身在内的所有资源都不会使用缓存。 浏览器直接发送 HTTP 请求且不带任何条件请求字段。
最后, 最佳实践还是我之前那篇,哈哈哈哈。
--END--
参考链接
网友评论