# ETag 监控：一个 no-cache 请求头改变了结果

Source: https://ipvolt.com/zh/blog/etag-monitoring-cache-control
Markdown: https://ipvolt.com/zh/blog/etag-monitoring-cache-control.md
Language: zh-CN

[ipvolt 首页](https://ipvolt.com/zh.md) / [博客](https://ipvolt.com/zh/blog.md) / ETag 监控：一个 no-cache 请求头改变了结果

分析
发布于: 2026-09-11
更新于: 2026-09-11
作者： ipvolt team
阅读约 2 分钟

一次真实的代理测试发现，仅仅一个请求头就把 304 响应变成了完整下载。本文查看实测的字节数、请求策略和可复现的数据记录。

在为内容监控购买更多代理流量之前，先检查你的客户端实际发送的请求头。在我们的线上网站上，添加 `Cache-Control: no-cache` 后，对未变更页面的检查从不带正文的 `304` 响应变成了完整的 `200` 下载，尽管请求中携带了匹配的 ETag。

在一次受控比较中，**12 个不带该覆盖项的条件请求传输了零字节响应正文；12 个带该覆盖项的请求传输了 271,968 字节未变更的 HTML。** 为获取 ETag 所需的 6 次初始下载另外耗费了 135,984 字节，并已计入公开的记录。

这是关于一个已部署的 Next.js 16.3.4 站点和一种特定请求策略的发现。它不是建议你从监控中去掉新鲜度要求。真正有用的决定是：是否更改客户端策略、选择更小的表示形式，或者调查服务器的重新验证行为。

## 任务：监控已知内容，同时避免不必要的下载

我们测试了两篇已有的 ipvolt 文章：[代理基准测试指南](/zh/blog/what-a-proxy-benchmark-should-measure)和[代理错误说明](/zh/blog/proxy-status-codes-407-429-502)，各自以 HTML 和 Markdown 两种形式。我们的监控器关注文章标题和两个指定的章节标题。它还检查媒体类型，对于 HTML，还检查页面标题和 canonical URL。

这些检查能确认被关注的信息存在。它们并不能让 Markdown 替代页面布局、JavaScript 行为或可正常工作的注册流程。确切的断言见[研究配置](https://ipvolt.com/downloads/content-monitoring/main-study.json)。

采集使用了面向美国、英国和荷兰的第三方认证代理连接。初始实验还包含一个直连对照组。国家检查响应在各检查点都报告了所请求的国家；在初始的美国运行期间出口地址发生过变化，因此这些是路线观察结果，而不是固定 IP 或全国范围的可靠性结果。连接提供商未予披露；这不是对可用的 ipvolt 代理服务的测试。

三项 HTTP 实验都在 2026 年 9 月 11 日运行。它们共发出 **234 个请求，传输了 2,975,066 字节响应正文**，包括初始下载、验证下载和未成立的假设。每一个尝试的请求都在没有 curl 传输错误的情况下完成。额外的地理探测和浏览器 QA 单独记录在[方法说明](https://ipvolt.com/downloads/content-monitoring/method.json)中；它们不在该 HTTP 实验总数之内。

## 更小的首次下载并没有回答缓存问题

初始实验一致地请求 gzip，并测量收到的正文字节数。每种表示形式各 12 次初始观察，大小如下：

| 文章表示形式 | 每次初始请求收到的正文字节数 |
| --- | ---: |
| 基准测试 HTML | 26,521 |
| 基准测试 Markdown | 7,056–7,061 |
| 错误指南 HTML | 18,807 |
| 错误指南 Markdown | 5,081 |

两个 HTML 端点都提供了 ETag。在本实验中，两个 Markdown 端点都没有提供 ETag 或 Last-Modified 验证器。不带 Cache-Control 覆盖项的 HTML 条件检查返回了 `304`；运行程序复用了此前已接受的匹配正文，而不是把空响应解释为一份新文档。

探索性运行发出了 144 个请求：120 个完整的 `200` 响应和 24 个被接受的 `304` 响应。它跳过了 36 个条件请求槽位，因为 Markdown 没有验证器，或者首页被标记为 `no-store`。被跳过的请求既不计为成功，也不计为零字节网络观察。

随后我们通过三条代理路线中的每一条，对每种表示形式连续运行了 5 次检查。每次检查都显式发送 `Cache-Control: no-cache`，客户端在检查之间保留了符合条件的验证器。全部 60 个请求都返回了 `200`，包括 24 次携带匹配 ETag 的 HTML 复检。「重复的 HTML 验证会避免下载正文」这一预期在该策略下并不成立。

## 隔离出改变响应的那个请求头

为了调查，我们保持 URL、路线配置、Accept、语言、gzip 偏好和确切的种子 ETag 不变。对于六种路线/文章组合中的每一种，我们发出一次种子请求和四次条件请求。顺序为 A/B/B/A 或 B/A/A/B，其中 A 不带 Cache-Control 覆盖项，B 发送 `no-cache`。

| 条件请求策略 | 尝试次数 | 响应 | 收到的正文字节数 |
| --- | ---: | --- | ---: |
| 匹配的 If-None-Match；无 Cache-Control 覆盖项 | 12 | 12 × 304 | 0 |
| 相同的 If-None-Match；Cache-Control: no-cache | 12 | 12 × 200 | 271,968 |

全部 24 次条件观察都与种子的关注值和解码后内容一致。算上 6 次种子请求，这次受控实验在 30 个请求中传输了 407,952 字节正文。它使用的是平衡排序而非随机抽样，确定的是这些端点在这段短暂窗口内观察到的行为。

实现代码解释了一个合理的机制。我们把实际部署的依赖文件与本地的 Next.js 16.3.4 安装进行了核对：相关源码哈希一致。它的 ETag 响应辅助函数在返回 `304` 之前会调用 `fresh`；捆绑的新鲜度检查在评估匹配的 ETag 之前，一旦请求包含 `no-cache` 就返回 false。一次单独的本地函数测试复现了同一分支。[Next.js 响应辅助函数](https://github.com/vercel/next.js/blob/v16.3.4/packages/next/src/server/send-payload.ts)、[fresh 实现](https://github.com/jshttp/fresh/blob/v0.5.2/index.js)。

这是与线上结果相符的、特定于技术栈的行为。HTTP 并没有把 `no-cache` 定义为一条要求下载完整正文的通用指令。收到 `304` 也不能证明请求到达了源站而不是某个作出响应的缓存。[HTTP 缓存](https://www.rfc-editor.org/rfc/rfc9111.html)。

## 有用的优化取决于你需要的新鲜度

对于显式使用 `no-cache` 的五次检查实验，实际总计如下：

| 表示形式 | 请求数（含初始下载） | 收到的正文字节数 |
| --- | ---: | ---: |
| HTML | 30 | 679,920 |
| Markdown | 30 | 182,070 |
| 差值 | 请求数相同 | 497,850 |

**在该策略下，对于被关注的内容，Markdown 传输的响应正文字节数少了 73.2%。** 它没有减少请求数。这些是实测的载荷差异，不是实测的账单节省，也不是对月度流量的预测。curl 的下载大小指标不包含请求头和其他网络开销。[curl 载荷统计](https://curl.se/libcurl/c/CURLINFO_SIZE_DOWNLOAD_T.html)。

当新鲜度要求允许时还有另一个选项：Markdown 响应声明了 `public, max-age=300, must-revalidate`。缓存可以在不发出请求的情况下复用仍然新鲜的已存储表示。缺少验证器并不会使其不可缓存。我们的运行程序有意通过网络进行检查；它们没有实现这种基于新鲜度的调度策略。[新鲜度与复用](https://www.rfc-editor.org/rfc/rfc9111.html#section-4)。

用这个结果来选择下一步动作：

| 你的监控需求 | 本案例支持的动作 |
| --- | --- |
| 可以接受在服务器的新鲜度窗口内复用 | 在安排另一次下载之前，评估一个感知新鲜度的本地缓存。 |
| 要求响应服务器或缓存对每次检查都进行验证 | 用实际端点测试最终的请求头；不要假设 ETag 保证得到不带正文的响应。 |
| 需要被关注的文本，而每次检查都返回完整正文 | 把压缩后的 HTML 与包含同样事实的文本或 Markdown 表示进行比较。 |
| 需要渲染后的布局或交互流程 | 保留浏览器检查；更小的文本表示覆盖不了这项工作。 |

不要仅仅为了复现零字节那一行而去掉 `no-cache`。两种请求头策略可能带来不同的新鲜度后果。先确定你的监控器必须检测什么，然后再对允许的实现进行测量。

## 复现结果并调整检查

把[离线分析脚本](https://ipvolt.com/downloads/content-monitoring/reproduce.py)和[脱敏后的记录](https://ipvolt.com/downloads/content-monitoring/recordings.zip)下载到同一目录。Python 3.10 或更高版本即可；此命令不发出任何网络请求：

```sh
python3 reproduce.py recordings.zip
```

它会重新计算尝试次数、正文总量、请求头比较和 Markdown 百分比。把它的输出与[记录的结果](https://ipvolt.com/downloads/content-monitoring/results.json)进行比较。数据集包含时间戳、请求策略、选定的响应头、内容检查和哈希值。凭据、出口 IP、未过滤的请求头和原始正文捕获仍然保密；回放验证的是公开的算术结果，而不是独立地对历史响应进行鉴真。

[下载说明](https://ipvolt.com/downloads/content-monitoring/README.md)链接了确切的采集程序、配置和 27 个通过的离线测试。线上程序需要 curl 8.4 或更高版本，保留 TLS 验证，对请求和正文传输设限，并通过 curl 的输入私密地传递代理认证。它们的允许列表仅限于本案例的公开站点；对于你自己运营的资产，请同时调整允许列表和内容断言。

我们没有观察到真实的内容更新，也没有测量检测延迟。所有测试端点的解码后内容都保持稳定；有少数 gzip 字节流在解码文本完全相同的情况下有所不同。这是一个应当比较监控器真正关心的内容的理由，而不是本研究消除了运营误报的证据。这只是一个站点、两篇文章和一段短暂的观察窗口——不是提供商排名，也不是通用的缓存基准。

*方法：智能体辅助的采集与分析，并有独立的证据、编辑和搜索审阅。所有报告的网络观察都是实际执行的；离线测试使用受控的测试数据，并单独标注。*

[访问开放时通知我](/zh/blog/etag-monitoring-cache-control#waitlist-blog-end)。开放时只发一封邮件，没有别的。

## 参考来源

- [RFC 9110: If-None-Match and conditional requests](https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.2)
- [RFC 9111: request and response cache directives](https://www.rfc-editor.org/rfc/rfc9111.html)
- [curl: downloaded response-body size](https://curl.se/libcurl/c/CURLINFO_SIZE_DOWNLOAD_T.html)
- [Next.js 16.3.4: ETag response handling](https://github.com/vercel/next.js/blob/v16.3.4/packages/next/src/server/send-payload.ts)
- [fresh 0.5.2: request freshness evaluation](https://github.com/jshttp/fresh/blob/v0.5.2/index.js)

## 第一时间了解 ipvolt 开放体验。

顺便一提

ipvolt 仍在开发中。留下邮箱，开放体验时我们只会通知你一次。

开放体验时仅发一封通知邮件，不发送其他邮件。

[申请抢先体验](https://ipvolt.com/zh/blog/etag-monitoring-cache-control#waitlist-blog-end)

[隐私政策](https://ipvolt.com/privacy)


## 相关文章

- [MostLogin 代理设置：添加、测试与团队共享](https://ipvolt.com/zh/blog/mostlogin-proxy-setup.md) (分析, 2026年9月17日, 阅读约 2 分钟): 逐步在 MostLogin 浏览器配置文件中添加代理：协议、主机和端口、账号密码、IP 检测、团队权限、批量导入，以及最常见的出错原因。
- [Amazon 价格：你的报价与 Featured Offer](https://ipvolt.com/zh/blog/amazon-offer-vs-featured-offer.md) (分析, 2026年9月16日, 阅读约 2 分钟): 使用匹配的上下文、已知运费以及一个经过测试、能保留缺失数据的离线模型，把你的 Amazon 报价与 Featured Offer 进行比较。
- [Amazon 商品信息更新：已接受不等于已生效](https://ipvolt.com/zh/blog/amazon-listing-update-reconciliation.md) (分析, 2026年9月14日, 阅读约 2 分钟): 通过区分已提交属性、在售报价、库存与可购买状态来诊断已被接受的 Amazon 商品信息更新，并附一张实用的对账矩阵供参考。

## 相关指南

- [在 curl 中使用代理：-x、环境变量、SOCKS5 与认证](https://ipvolt.com/zh/guides/curl-proxy-setup.md): 如何在 curl 中使用代理：-x 参数、http_proxy 与 https_proxy 变量、通过 socks5h 使用 SOCKS5、代理认证，以及如何解读 CONNECT 与 407 错误。
- [Python Requests 代理配置：认证与 SOCKS5](https://ipvolt.com/zh/guides/python-requests-proxy.md): 在 Python Requests 中配置代理：proxies 字典、Session 默认值、凭据、通过 requests[socks] 使用 SOCKS5、环境变量与 ProxyError。
- [一次一个阶段地排查代理超时](https://ipvolt.com/zh/guides/proxy-timeout-troubleshooting.md): 用 curl 计时把代理 DNS、TCP、CONNECT、TLS 和响应延迟分开，然后设置请求截止时间，并判断重试是否安全。

## 关于 ipvolt

来自 ipvolt 团队的技术分析。

ipvolt 尚未开放使用。

[阅读英文原文](https://ipvolt.com/blog/etag-monitoring-cache-control.md)
