HTTP缓存

HTTP缓存主要分为强缓存和协商缓存,强缓存通过Expires和Cache-Control头部来设置资源的有效期,避免每次请求都向服务器验证。协商缓存则涉及Last-Modified和If-Modified-Since或Etag与If-None-Match的配合,只有在资源过期时才向服务器确认是否更新。Etag的优先级高于Last-Modified。
摘要由CSDN通过智能技术生成


HTTP 缓存策略分为两种: 强缓存协商缓存 ,这两种缓存策略都是服务端设置 HTTP Header 来实现的
在这里插入图片描述


强缓存

服务器通过设置 http 中 hdader 的 Expires 和 Cache-Control 字段告诉浏览器缓存的有效期。这种方法会有一个固定时间,所带来的问题是如果服务器数据进行了更新,但是还没有到强缓存的过期时间,则数据无法更新。

可以通过三种方式来设置强缓存:


Expires

Expires:服务端在响应头中设置一个 GMT 格式的到期时间。客户端的本地时间小于响应头的 Expires 时间,那么会从本地进行读取,不会去请求服务器。如果超过了,那么就去请求服务器去获取最新资源。

但是就是因为根据本地时间进行判断,本地时间可以随便修改,所以这种缓存机制有漏洞,会与服务端时间有偏差,为了解决这个问题,就出现了下面的 Cache-Control。


Cache-Control

Cache-Control:他和Expires不一样,Expires是直接设置一个时间戳就行了,而Cache-Control可以设置下面这几种属性:

可缓存性

  • public(这个表示缓存既可以被浏览器缓存,也可以被代理服务器缓存)
    表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存,即使是通常不可缓存的内容。(例如:1.该响应没有max-age指令或Expires消息头;2. 该响应对应的请求方法是 POST 。)

  • private(这个表示缓存只能被客户端的浏览器缓存,不能被代理服务器缓存)
    表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。私有缓存可以缓存响应内容,比如:对应用户的本地浏览器。

  • no-store(这个属性表示不缓存,在任何情况下,都是与服务器进行最新的交互)
    缓存不应存储有关客户端请求或服务器响应的任何内容,即不使用任何缓存。

  • no-cache(这个并非不缓存的意思,这个表示强制进行协商缓存,会在下面描述)
    在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证 (协商缓存验证)。

到期

  • max-age=<seconds>
    设置缓存存储的最大周期,超过这个时间缓存被认为过期 (单位秒)。与Expires相反,时间是相对于请求的时间。例如设置 max-age=30 表示客户端时间向后滑动30秒,在这30秒内都是强缓存,不会去请求服务器。

  • s-maxage=<seconds>
    这个和上面的一样,只不过这个设置的是代理服务器的缓存时间。

  • max-stale[=<seconds>]
    表明客户端愿意接收一个已经过期的资源。可以设置一个可选的秒数,表示响应不能已经过时超过该给定的时间。

  • min-fresh=<seconds>
    表示客户端希望获取一个能在指定的秒数内保持其最新状态的响应。

  • stale-while-revalidate=<seconds> 实验性
    表明客户端愿意接受陈旧的响应,同时在后台异步检查新的响应。秒值指示客户愿意接受陈旧响应的时间长度。

  • stale-if-error=<seconds> 实验性
    表示如果新的检查失败,则客户愿意接受陈旧的响应。秒数值表示客户在初始到期后愿意接受陈旧响应的时间。

重新验证和重新加载

  • must-revalidate
    一旦资源过期(比如已经超过max-age),在成功向原始服务器验证之前,缓存不能用该资源响应后续请求。

  • proxy-revalidate
    与 must-revalidate 作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。

  • immutable 实验性
    表示响应正文不会随时间而改变。资源(如果未过期)在服务器上不发生改变,因此客户端不应发送重新验证请求头(例如If-None-Match或 If-Modified-Since)来检查更新,即使用户显式地刷新页面。在 Firefox 中,immutable 只能被用在 https:// transactions. 有关更多信息,请参阅这里。

其他

  • no-transform
    不得对资源进行转换或转变。Content-Encoding、Content-Range、Content-Type等 HTTP 头不能由代理修改。例如,非透明代理或者如Google’s Light Mode可能对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。no-transform指令不允许这样做。

  • only-if-cached
    表明客户端只接受已缓存的响应,并且不要向原始服务器检查是否有更新的拷贝。


pragma(了解)

Pragma 的值为 no-cache : 表示禁用缓存


优先级

当 http 响应头中同时出现 PragmaCache-Control , 缓存控制执行优先级从高到低是
Pragma => Cache-Control => Expires


协商缓存

协商缓存表示在使用本地的缓存之前,会先向服务器发一个请求,与服务器协商当前浏览器的缓存是否已经过期了,如果没过期,那么就使用本地的资源,如果过期了就去请求最新资源。协商缓存主要是解决强缓存资源不能及时更新的问题,协商缓存服务端可以通过2种设置来实现:
注意:协商缓存需要设置Cache-Control为no-cache,表示设置成协商缓存

Last-Modified 配合 If-Modified-Since

例如,客户端请求一个 1.jpg,服务端接收到这个请求后,会读取这个文件的最后修改时间,然后设置到响应头中,设置的参数就是 Last-Modified,参数值是文件最后修改的时间戳。客户端第二次请求 1.jpg 这个文件的时候,会带上一个 If-Modified-Since 参数,服务端能拿到这个参数与last-modified进行比对,如果一致,那么就返回304状态,否则就去请求最新的文件,使用nodejs实现这个代码:

// 判断客户端请求的是03这个图片
if (pathname === '/img/1.jpg') {
  // 读取 03 图片的最后修改时间
  const { mtime } = fs.statSync('./img/1.jpg');
  // 判断客户端发送过来的if-modified-since是否与mtime一致,如果一致就直接返回304
  if (req.headers['If-Modified-Since'] === mtime.toUTCString()) {
    res.statusCode = 304;
    res.end();
  } else {
    // 如果不一致,那么就请求最新的资源返回给客户端
    const data = fs.readFileSync('./img/1.jpg');
    // 这2句代码是设置协商缓存
    res.setHeader('Last-Modified', mtime.toUTCString());
    res.setHeader('Cache-Control', 'no-cache');
    res.end(data);
  }
}

上面的 Last-Modified 配合 If-Modified-Since 在使用时有些弊端,例如将 1.jpg修改成 2.jpg,再改回 1.jpg。此时这个文件其实是没有变化的,但是最后修改时间更改了,因此客户端就需要重新请求,因此就出现了下面的第二种使用Etag的方式


Etag 配合 If-None-Match

Etag 实现的方式是服务端为文件生成一个指纹,类似于MD5字符串。接着响应头中塞进 Etag 参数,参数的值就是计算出的字符串,客户端接收到后,第二次请求会带上一个 If-None-Match 的参数,接着服务端和上面第一种方式一样进行比对,nodejs的实现代码如下:

// 引入 etag 模块
const etag = reqiure("etag")
if (pathname === '/img/1.jpg') {
  const data = fs.readFileSync('./img/1.jpg');
  // 获取生成的etag字符串
  const etag = etag(data);
  // 判断客户端发送的 If-None-Match 与服务端是否一致
  if (req.headers['If-None-Match'] === etag) {
    res.statusCode = 304;
    res.end();
  } else {
    // 如果不一致,那么就请求最新的资源返回给客户端
    const data = fs.readFileSync('./img/1.jpg');
    // 这2句代码是设置协商缓存
    res.setHeader('Etag', etag);
    res.setHeader('Cache-Control', 'no-cache');
    res.end(data);
  }
}

Etag计算


Nginx

Nginx官方默认的ETag计算方式是为“文件最后修改时间16进制-文件长度16进制”。
例:Etag: “59e72c84-2404”


Express

Express框架使用了serve-static中间件来配置缓存方案,其中,使用了一个叫etag的npm包来实现etag计算。从其源码可以看出,有两种计算方式:

方式一:使用文件大小和修改时间

function stattag (stat) {
  var mtime = stat.mtime.getTime().toString(16)
  var size = stat.size.toString(16)

  return '"' + size + '-' + mtime + '"'
}

方式二:使用文件内容的hash值和内容长度

function entitytag (entity) {
  if (entity.length === 0) {
    // fast-path empty
    return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"'
  }

  // compute hash of entity
  var hash = crypto
    .createHash('sha1')
    .update(entity, 'utf8')
    .digest('base64')
    .substring(0, 27)

  // compute length of entity
  var len = typeof entity === 'string'
    ? Buffer.byteLength(entity, 'utf8')
    : entity.length

  return '"' + len.toString(16) + '-' + hash + '"'
}

优先级

当 Last-Modified 和 Etag 属性同时出现的时候,Etag 的优先级更高



参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__畫戟__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值