适用场景
维护网站时,想知道同一个资源没有变化时是否还会重复传输正文,可以用 curl 做两次有对照的请求。本教程把首次正文和第二次响应分开保存,观察服务器能否利用实体标签确认资源未变,并解释为什么有时第二次仍返回完整正文。
适合在 Linux 终端中学习 HTTP 缓存验证,也适合检查自己有权维护的网站公开静态资源。实验只做少量普通读取请求,不需要登录信息,不修改服务器配置。
原理
实体标签(ETag)是服务器给某个资源表示提供的验证标识。客户端保存标签后,在下次请求中发送条件字段 If-None-Match。对于本教程使用的读取请求,如果条件判定资源未变,服务器可以返回未修改状态码三百零四,而不传输资源正文;需要返回新表示时,通常返回二百及正文。
标签应按服务器返回的形式原样保存,包括引号和可能存在的弱标签前缀。它不一定是文件哈希,也不能作为内容安全校验值。相关条件请求、标签和三百零四语义见 HTTP 语义标准 RFC 9110。
curl 提供标签保存与比较选项,可以避免手工拼接引号。它不会因此自动成为完整的浏览器缓存管理器:本地正文如何保存、何时替换,仍由使用者负责。
准备工作
准备 curl 七点六十八或更高版本,以及一个新的空练习目录。先检查版本:
curl --version
下面默认请求 RFC 9110 的公开文本页面,它是真实资料地址,但服务端策略可能变化,因此教程不预先保证它一定返回标签。若响应不提供标签,可按提示输入自己有权检查、明确支持实体标签的公开静态资源完整 HTTPS 地址。
不使用需要认证的页面,也不要选择含个人资料的链接。实验目录中的文件名都用于保存本次观察结果,确保没有同名重要文件。
分步骤操作
第一步:确定同一个资源地址
在 Linux 的 Bash 终端执行:
read -r -p '请输入公开静态资源的 HTTPS 地址,回车使用 RFC 文本:' resource_url
resource_url=${resource_url:-https://www.rfc-editor.org/rfc/rfc9110.txt}
resource_url 保存实际请求地址。后续两次请求必须使用相同地址和相同的表示选择条件,例如不要中途改变语言、认证身份或压缩相关请求头,否则比较的可能不是同一种表示。
第二步:保存首次响应与标签
执行首次请求:
curl --silent --show-error --connect-timeout 10 --max-time 30 \
--dump-header first.headers \
--etag-save resource.etag \
--output first.body \
--write-out 'HTTP=%{http_code} body_bytes=%{size_download}\n' \
"$resource_url"
printf 'curl退出码:%s\n' "$?"
首次响应头保存为 first.headers,正文保存为 first.body,标签保存为 resource.etag。连接等待和总时限分别限制在十秒与三十秒。立即读取退出码,是为了区分网络传输失败和服务器返回的 HTTP 状态。
查看结果:
sed -n '1,35p' first.headers
cat resource.etag
wc -c first.body
只有首次请求成功取得二百响应、正文完整且标签文件非空,才进入下一步。没有标签时,curl 会创建空标签文件;这不能作为有效的条件验证依据。相关选项行为见 curl 官方手册。
第三步:发送带标签的条件请求
保持同一终端变量不变,执行:
curl --silent --show-error --connect-timeout 10 --max-time 30 \
--etag-compare resource.etag \
--dump-header second.headers \
--output second.body \
--write-out 'HTTP=%{http_code} body_bytes=%{size_download}\n' \
"$resource_url"
printf 'curl退出码:%s\n' "$?"
sed -n '1,35p' second.headers
比较选项会读取已保存的标签并构造条件请求。这里故意使用不同正文文件名,首次保存的资源不会被第二次请求的结果覆盖。本次也没有更新原标签,便于保留第一次比较的基准。
第四步:结合状态与下载量解释结果
如果第二次显示三百零四,且正文下载量为零,就说明本次条件验证判定当前表示未变。客户端可以继续使用自己保存的首次正文,不应把第二次响应当成一份新的完整文档。
如果第二次返回二百并下载正文,可能是资源已经变化、服务器没有采用该验证条件,或请求实际上选择了不同表示。先比较两次响应头中的实体标签和相关选择信息,再考虑服务器实现,不要直接判断缓存故障。
如果返回重定向,先查看位置字段 Location,确认新地址仍是预期公开资源,再用最终地址重新完成两次实验。本例没有自动跟随跳转,避免把多个响应头混在一起影响初学者判断。
验证方法
将首次状态码、标签、正文大小,与第二次状态码和正文下载量放在一起记录。一次有效的未变验证,需要有真实的首次标签,也需要第二次确实得到三百零四;只看到标签文件存在并不够。
正文下载量不包含全部协议开销,因此零正文并不等于网络完全没有流量。三百零四也只说明这一次服务器验证的结果,不能证明所有浏览器已经采用理想缓存策略,或资源可以永久不再检查。
常见错误
手工删除标签中的引号,可能导致条件格式错误。更换地址后继续使用旧标签,则失去了清晰的比较对象。首次得到错误页却继续实验,也会把错误页的行为误当成目标资源行为。
curl 默认不会把所有 HTTP 错误状态都转换成非零退出码,因此必须同时查看状态码与退出码。第二次没有正文时,输出文件在不同情形下可能为空或没有新内容,不应据此覆盖首次正文。
安全与回退
保留正常的 HTTPS 证书验证;证书错误时先检查时间、网络和证书链,不关闭校验。对公开服务只做必要次数的请求,不编写高频循环。
本实验没有修改远端缓存或服务端配置。结束后关闭终端,并仅删除本次练习目录中确认不再需要的响应头、正文和标签文件即可。若要把机制加入正式下载脚本,应另行设计状态判断和文件替换流程,保证只有完整的新正文才替换现有文件。
参考资料与转载说明
本文依据上述公开资料进行中文原创整理,不是逐句翻译,也不替代厂商手册。操作前请核对自己的版本、型号和备份。
评论一下?