你知道 http 响应头中的 ETag 是如何生成的吗

10,950 阅读2分钟

关于 etag 的生成需要满足几个条件

  1. 当文件不会更改时,etag 值保持不变。所以不能单纯使用 inode
  2. 便于计算,不会特别耗 CPU。这样子 hash 不是特别合适
  3. 便于横向扩展,多个 node 上生成的 etag 值一致。这样子 inode 就排除了

关于服务器中 etag 如何生成可以参考 HTTP: Generating ETag Header

那么在 nginx 中的 etag 是如何生成的?

nginx 中 ETag 的生成

我在网上找到一些资料与源代码了解到了 etag 的计算方法。由 python 伪代码表示计算方法如下

etag = '{:x}-{:x}'.format(header.last_modified, header.content_lenth)

源码: ngx_http_core_modules.c

etag->value.len = ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
                                  r->headers_out.last_modified_time,
                                  r->headers_out.content_length_n)
                      - etag->value.data;

总结:nginxetag 由响应头的 Last-ModifiedContent-Length 表示为十六进制组合而成。

随手在我的k8s集群里找个 nginx 服务测试一下

$ curl --head 10.97.109.49
HTTP/1.1 200 OK
Server: nginx/1.16.0
Date: Tue, 10 Dec 2019 06:45:24 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 23 Apr 2019 10:18:21 GMT
Connection: keep-alive
ETag: "5cbee66d-264"
Accept-Ranges: bytes

etag 计算 Last-ModifiedContent-Length,使用 js 计算如下,结果相符

> new Date(parseInt('5cbee66d', 16) * 1000).toJSON()
"2019-04-23T10:18:21.000Z"
> parseInt('264', 16)
612

Last-Modified,ETag 与协商缓存

我们知道协商缓存有两种方式

  • Last-Modified/if-Modified-Since
  • ETag/If-None-Match

既然在 nginxETagLast-ModifiedContent-Length 组成,那它便算是一个加强版的 Last-Modified 了,那加强在什么地方呢?

Last-Modified 是由一个 unix timestamp 表示,则意味着它只能作用于秒级的改变

那下一个问题:如果 http 响应头中 ETag 值改变了,是否意味着文件内容一定已经更改


我是山月,你可以添加我微信 shanyue94 与我交流

如果你对全栈面试,前端工程化,graphql,devops,个人服务器运维以及微服务感兴趣的话,可以关注我