Last-Modified
针对静态资源而言,一般会选择文件的 mtime
元属性作为上次修改时间,该元属性表示文件内容的修改时间。
nginx
也是这样处理的,源码见: ngx_http_static_module.c
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = of.size;
r->headers_out.last_modified_time = of.mtime;
但是,Last-Modified
拥有一定的局限性
- 时间精读只有秒级别,但某文件在一秒内被更改 N 次
- 某一文件经过修改后,但是文件内容并未发生变化,比如添加一行再删除该行
ETag
ETag 拥有比 Last-Modified 更高的精读,从 HTTP 规范上来讲,可以解决以上问题。
比如针对文件内容进行 hash 计算,得到 hash 值作为 ETag
的值。
但是从实现上而言,往往不使用会消耗大量 CPU 的 hash 计算,比如 nginx,源码见: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;
nginx 中 ETag 由响应头的 Last-Modified 与 Content-Length 表示为十六进制组合而成。
$ docker exec -it $(docker run -d nginx:alpine) curl --head localhost
HTTP/1.1 200 OK
Server: nginx/1.23.0
Date: Tue, 13 Sep 2022 09:24:39 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 21 Jun 2022 17:06:59 GMT
Connection: keep-alive
ETag: "62b1fab3-267"
Accept-Ranges: bytes
正因如此,即便 http 响应头中 ETag 值改变了,也不意味着文件内容一定已更改
实例
百度首页使用了 Nginx 类似的 ETag 生成算法,我在 Apifox 中进行了演示。见文档。
作业
- ETag 值是如何生成的
- ETag 与 Last-Modified 有何区别
- 如果 http 响应头中 ETag 值改变了,是否意味着文件内容一定已经更改