深入了解HTTP缓存机制:强制缓存和协商缓存的实现及控制

51 阅读4分钟

HTTP缓存是一种用于优化网站性能和减少网络流量的重要机制。通过使用HTTP缓存,浏览器可以在再次访问同一网站时,避免重新请求相同的资源,而是从本地缓存中加载资源。这不仅可以加速网站加载速度,还可以减少网络流量和服务器负载。

在本文中,我将介绍HTTP缓存的工作原理以及如何使用HTTP头来控制缓存。我还将提供一些示例代码,以帮助您更好地理解HTTP缓存的实际实现。

HTTP缓存的工作原理

HTTP缓存分为两种类型:强制缓存和协商缓存。当浏览器第一次请求资源时,服务器可以使用HTTP响应头控制缓存策略。

强制缓存

强制缓存是指在指定时间内,浏览器不会再向服务器发送请求,而是从本地缓存中加载资源。这可以通过在HTTP响应头中设置Cache-ControlExpires字段来实现。

Cache-Control字段是HTTP/1.1中最常用的控制缓存的头字段。它支持一系列的指令,包括max-ageno-cacheno-store等。max-age指令表示资源在缓存中的有效时间,单位为秒。例如,如果您希望浏览器缓存资源1个小时,您可以设置Cache-Control: max-age=3600no-cache指令表示浏览器必须与服务器确认资源是否已更改。no-store指令表示缓存不应存储任何版本的请求或响应。

Expires字段是HTTP/1.0中用于控制缓存的头字段。它表示资源在缓存中的过期时间,以GMT格式的日期表示。例如,Expires: Tue, 10 Jul 2023 08:44:30 GMT表示资源在此日期之后过期。如果同时设置了Cache-Control: max-ageExpires,则Cache-Control会覆盖Expires

协商缓存

协商缓存是指浏览器向服务器发送条件请求,服务器根据资源是否更改返回304 Not Modified或200 OK响应。协商缓存可以通过在HTTP响应头中设置ETagLast-Modified字段来实现。

ETag是服务器为每个资源分配的唯一标识符。当浏览器请求资源时,服务器会在HTTP响应头中返回ETag字段。浏览器在再次请求相同资源时,将在HTTP请求头中包含If-None-Match字段,并将上次收到的ETag值作为参数。如果资源未更改,服务器将返回304 Not Modified响应。

Last-Modified字段是资源上次修改的时间。当浏览器请求资源时,服务器会在HTTP响应头中返回Last-Modified字段。浏览器在再次请求相同资源时,将在HTTP请求头中包含If-Modified-Since字段,并将上次收到的Last-Modified值作为参数。如果资源未更改,服务器将返回304 Not Modified响应。

控制HTTP缓存

为了控制HTTP缓存,我们可以在服务器端设置HTTP响应头。下面是一些常用的HTTP头字段及其作用:

  • Cache-Control: 控制强制缓存的行为,例如设置max-age指令来控制资源在缓存中的有效时间。
  • Expires: 控制强制缓存的过期时间,但是已被Cache-Control所取代。
  • ETag: 控制协商缓存的唯一标识符。
  • Last-Modified: 控制协商缓存的资源上次修改时间。

以下是一些示例代码,展示如何在Node.js中设置HTTP响应头:

// 强制缓存示例
app.get('/static/image.png', (req, res) => {
  // 设置缓存时间为1小时
  res.setHeader('Cache-Control', 'max-age=3600');
  // 或者使用Expires字段,表示资源在1小时后过期
  // res.setHeader('Expires', new Date(Date.now() + 3600 * 1000).toUTCString());

  // 发送图片文件
  res.sendFile('/path/to/image.png');
});

// 协商缓存示例
app.get('/api/data.json', (req, res) => {
  const data = { foo: 'bar' };
  const lastModified = new Date('2022-05-09T00:00:00.000Z');

  // 设置Last-Modified头字段
  res.setHeader('Last-Modified', lastModified.toUTCString());

  // 如果浏览器发送了If-Modified-Since头字段,并且上次修改时间与资源相同,返回304 Not Modified
  if (req.headers['if-modified-since'] && new Date(req.headers['if-modified-since']).getTime() === lastModified.getTime()) {
    return res.status(304).end();
  }

  // 如果资源未更改,返回200 OK响应,否则返回数据和ETag头字段
  const etag = crypto.createHash('md5').update(JSON.stringify(data)).digest('hex');
  if (req.headers['if-none-match'] === etag) {
    return res.status(304).end();
  } else {
    res.setHeader('ETag', etag);
    return res.json(data);
  }
});

结论

HTTP缓存是一种优化网站性能和减少网络流量的重要机制。我们可以使用HTTP头字段来控制缓存策略,包括强制缓存和协商缓存。在实际开发中,我们应该根据实际情况选择适当的缓存策略,以提高用户体验和降低服务器负载。