🏠 首页 攻略 ETag是什么?HTTP缓存的"防伪标签"

ETag是什么?HTTP缓存的"防伪标签"

一文讲透ETag原理:为什么你的网页刷新快、图片加载秒出?了解ETag的工作机制、使用方法和常见误区。

什么是ETag?

ETag,全称Entity Tag,是HTTP协议里的一种缓存验证机制。

你可以把它理解为商品的"防伪标签"。超市里的每件商品都有一个独一无二的编号。你买的时候记住这个编号,下次来的时候先告诉店员"我看看编号XXX的东西有没有变"——店员不用把整件商品拿给你看,只需要核对一下编号就行。

如果编号对应的东西没变,店员直接说"还是原来那个",你就省了搬东西的力气。如果变了,再重新拿给你。

ETag就是干这个活的。服务器给每个资源分配一个唯一标识符,客户端缓存了这个标识符之后,再次请求时把它发给服务器。服务器比较一下:资源变了吗?没变就返回304(不用传数据),变了就返回200和新数据。

ETag有什么用?

节省带宽是最直接的好处。

假设你访问一个10MB的图片网站。第一次加载,服务器返回图片的同时也返回一个ETag值,比如"a1b2c3d4"。你关掉浏览器,第二天再来。

没有ETag的话:浏览器重新下载10MB图片。

有ETag的话:浏览器发请求时带上If-None-Match: "a1b2c3d4"。服务器一看,图片没改过,直接回一个304状态码,只传几十个字节的头部信息。10MB的数据不用重传。

对于频繁访问的网站,这个优化效果非常明显。

另一个作用是精确缓存。之前有个概念叫Last-Modified(最后修改时间),但它的精度只有秒级。如果文件在1秒内被修改了两次,Last-Modified就分辨不出来了。ETag是服务器自定义的字符串,可以精确到任何粒度。

ETag怎么用?

服务器端

服务器在返回资源时,顺便返回ETag:

HTTP/1.1 200 OK
ETag: "v2.3-final"
Content-Type: image/png
Content-Length: 102400

ETag的值可以是任何字符串。常见做法有两种:

  1. 版本号:手动指定,适合静态资源。文件改名或更新时改版本号。
  2. 哈希值:对文件内容计算MD5或SHA,自动保证内容变化时ETag一定变化。

客户端

浏览器会自动处理ETag逻辑,你不需要写代码。但如果你是开发者,用curl测试很方便:

# 第一次请求,拿到ETag
curl -I https://example.com/image.png

# 第二次请求,带上ETag
curl -I -H 'If-None-Match: "v2.3-final"' https://example.com/image.png

如果服务器返回304,说明资源没变。返回200说明资源更新了。

前端开发中的注意事项

如果你自己用JavaScript发请求,可以用fetch API配合ETag:

// 从缓存读取ETag
const cachedEtag = localStorage.getItem('my-resource-etag');

fetch('/api/data', {
  headers: cachedEtag ? { 'If-None-Match': cachedEtag } : {}
})
.then(response => {
  if (response.status === 304) {
    console.log('数据未更新,使用缓存');
  } else {
    const newEtag = response.headers.get('etag');
    localStorage.setItem('my-resource-etag', newEtag);
    return response.json();
  }
});

常见坑

弱ETag和强ETag:ETag前面加W/表示弱校验(如W/"abc")。弱ETag意味着两个资源的语义相同但二进制不同,比如两份格式略有差异但内容一致的JSON。强ETag要求完全一致。大多数情况下用强ETag就够了。

动态内容的ETag:如果页面每次请求内容都不同(比如带用户信息的个人中心页),就别用ETag了。缓存验证只会浪费一次网络往返。

CDN和ETag:有些CDN会覆盖服务器返回的ETag,或者自己生成一个。部署时要确认CDN的行为是否符合预期。

总结

ETag是HTTP缓存体系中的重要一环。它像商品的防伪标签,让客户端能快速判断资源是否过期,避免不必要的重复传输。

配合Last-Modified一起用时效果更好——Last-Modified负责粗筛,ETag负责精判。两者结合,既省带宽又保准确。

下次打开浏览器开发者工具的Network面板,看看请求头里的ETag和If-None-Match字段,你就能直观感受到它在默默工作。