什么是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的值可以是任何字符串。常见做法有两种:
- 版本号:手动指定,适合静态资源。文件改名或更新时改版本号。
- 哈希值:对文件内容计算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字段,你就能直观感受到它在默默工作。