返回

深入理解 HTTP 中的 ETag:掌控 Web 缓存机制

前端

ETag,全称 Entity Tag,是 HTTP 协议中用于标识请求资源最新版本的元数据。通过使用 ETag,浏览器和服务器可以判断资源是否已被修改,从而实现高效的缓存机制和避免不必要的资源重新下载。

一、ETag 的原理

ETag 本质上是一个唯一字符串,由服务器为每个请求的资源生成。当资源被修改时,ETag 值也会随之改变。当浏览器向服务器发送请求时,会附带请求头中的 ETag 值。如果服务器端 ETag 与请求中的 ETag 相匹配,则表明资源未被修改,服务器将返回 304 Not Modified 状态码,浏览器将使用本地缓存的资源。

二、ETag 的优势

使用 ETag 有以下优势:

  • 提高性能: 通过避免不必要的资源重新下载,ETag 可显着提高 Web 应用程序的性能。
  • 减少带宽消耗: 仅当资源被修改时,才需要重新下载,从而节省了带宽。
  • 增强用户体验: 通过快速响应,ETag 可以改善用户体验,特别是对于需要频繁更新的动态内容。

三、ETag 的局限性

尽管 ETag 非常有用,但也有一些局限性:

  • 不适用于所有资源: ETag 仅适用于可缓存的资源,例如图像、CSS 和 JavaScript 文件。
  • 可能出现冲突: 如果多个客户端同时修改同一资源,可能会导致 ETag 冲突。
  • 可能被绕过: 如果客户端禁用缓存或故意修改 ETag 值,则 ETag 机制将失效。

四、在实践中使用 ETag

在实际开发中,我们可以通过以下步骤来使用 ETag:

  1. 生成 ETag: 服务器端为每个资源生成唯一的 ETag 值。
  2. 发送 ETag: 在 HTTP 响应头中包含 ETag 值。
  3. 检查 ETag: 当浏览器再次请求该资源时,它会将请求头中的 ETag 值发送给服务器。
  4. 比较 ETag: 服务器比较请求中的 ETag 值和服务器端的 ETag 值。
  5. 响应: 如果 ETag 匹配,服务器返回 304 状态码,否则返回资源内容和更新的 ETag 值。

五、示例代码

以下是使用 ETag 的示例代码:

服务器端(Python):

import hashlib

def generate_etag(resource):
    """生成资源的 ETag 值。"""
    m = hashlib.sha1()
    m.update(resource.encode("utf-8"))
    return m.hexdigest()

@app.route("/")
def index():
    resource = "Hello World!"
    etag = generate_etag(resource)

    if request.headers.get("If-None-Match") == etag:
        return "", 304
    else:
        return resource, 200

客户端端(JavaScript):

fetch("/resource")
  .then(response => {
    if (response.status === 304) {
      // 资源未修改,使用本地缓存。
    } else {
      // 资源已修改,更新本地缓存。
      response.text().then(data => {
        // 处理更新后的资源数据。
      });
    }
  });