bigywater.tech
Flask · 113

ETag API 缓存

ETag 到底省了什么?用正文哈希做 ETag 是不是真的快?If-None-Match 与 Last-Modified 怎么配合?这篇用慢查询把 304 的耗时和带宽量出来。

64 → 0.41 ms
同样是 304:哈希 ETag 与版本号 ETag 的耗时(慢查询 50 ms)
48039 → 0 字节
200 与 304 的响应体大小
SHA-1
Werkzeug add_etag 对正文计算的哈希

304 与空正文

实测 · 本机执行输出
  第一次 → 200 | ETag = "ecea853257b1cff7d9816bd5975acb07cde13cf3" | 正文 48039 字节
  带 If-None-Match → 304 | 正文 0 字节 | ETag 头仍在: True
  If-None-Match 是别的值 → 200 | 正文 48039 字节
  If-None-Match 是列表 "x", ETag, "y" → 304
  POST 带 If-None-Match → 405 (路由只允许 GET)
  ETag 是正文的哈希吗: True (Werkzeug 的 add_etag 用正文的 SHA-1 十六进制)

上一篇:CORS 预检请求;下一篇:OpenAPI 文档生成。

哈希与版本号

实测 · 本机执行输出
  哈希 ETag:200(没带头)           59.88 ms  状态 200  正文 48039 字节
  哈希 ETag:304                63.63 ms  状态 304  正文 0 字节
  版本号 ETag:200               55.73 ms  状态 200  正文 48039 字节
  版本号 ETag:304                0.41 ms  状态 304  正文 0 字节
  数据变了(版本号 → v2)后,带旧 ETag 的请求 → 200 新 ETag "v2"

Last-Modified

实测 · 本机执行输出
  第一次 → 200 | Last-Modified = Thu, 01 Jan 2026 00:00:00 GMT
  带 If-Modified-Since(同一时间)→ 304
  If-Modified-Since 是更早的时间 → 200

弱 ETag

实测 · 本机执行输出
  ETag = W/"abc"
  If-None-Match: W/"abc" → 304 | If-None-Match: "abc"(强)→ 304 (If-None-Match 用弱比较,两者都匹配)

带宽

实测 · 本机执行输出
  200 响应体 48039 字节;304 响应体 0 字节;头部总字节(估算)200: 142,304: 87

分步演示

ETag API 缓存
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动
    • 没有验证:真实浏览器里的缓存行为与 Cache-Control(max-age、no-cache、must-revalidate)的交互、CDN 与 Vary 的配合、gzip 压缩对 ETag 的影响、If-Match 与 ETag 的并发控制(见第 94 篇)、分页列表整体 ETag 的失效粒度
    京ICP备2026067502号-1京公网安备11010502063487号