首页 / 资讯中心 / 文章详情

HTTP 缓存怎么配:no-cache 到底缓不缓、ETag 和 Last-Modified 谁优先

HTTP 缓存怎么配:no-cache 到底缓不缓、ETag 和 Last-Modified 谁优先 ★ FEATURED ARTICLE
本文首发于 CSDN转载请注明出处。先说结论no-cache不是不缓存它的意思是可以存但每次用之前必须回源验证真正禁止存储的是no-store。这一条被写错的文章非常多而它恰好决定了线上事故的性质——把该用no-store的地方写成no-cache敏感数据会留在磁盘上被缓存复用。强缓存和协商缓存分别管什么浏览器以及 CDN、反向代理判断要不要用本地副本有两条路径层级机制是否发请求谁决定有效期强缓存新鲜度Cache-Control: max-age、Expires不发响应头里的时间协商缓存验证ETagIf-None-Match、Last-ModifiedIf-Modified-Since发条件请求源服务器返回 304规范对新鲜的定义RFC 9111是响应未超过它的新鲜期就可以直接复用无需联系源服务器。这是一切性能收益的来源——命中强缓存时网络面板上只有一个 “(from disk cache)”没有请求。超出新鲜期后才轮到协商缓存上场浏览器带上验证器问服务端这个还能用吗能就回304 Not Modified不带响应体不能就回完整的200。no-cache和no-store差在哪这是本篇最重要的部分。看 RFC 9111 的两条原文no-cache§5.2.2.4The no-cache response directive, in its unqualified form (without an argument), indicates that the response MUST NOT be used to satisfy any other request without forwarding it for validation and receiving a successful response.no-store§5.2.2.5The no-store response directive indicates that a cache MUST NOT store any part of either the immediate request or the response and MUST NOT use the response to satisfy any other request.对照着读差别很清楚指令允许存储每次复用前需验证适用场景no-cache是是内容会变但要求必须最新如首页 HTMLno-store否—敏感数据账户信息、支付页、一次性 tokenMDN 对此有一段非常直白的提示Note that no-cache does not mean “don’t cache”. no-cache allows caches to store a response but requires them to revalidate it before reuse. If the sense of “don’t cache” that you want is actually “don’t store”, then no-store is the directive to use.no-store的含义还要更严一层规范里的定义包含尽最大努力尽快从易失性存储中移除这层要求。所以在共享机器、代理链路上no-cache的响应体是实打实被写进磁盘的no-store才不是。判断口诀要每次都最新用no-cache要不许留痕用no-store。剩下四个指令各自的边界指令官方语义容易搞错的点max-ageNN 秒后视为陈旧是从生成响应时算起不是从收到时s-maxageN仅覆盖共享缓存的有效期会同时覆盖max-age和Expiresprivate共享缓存不得存储浏览器本地缓存可以存public明确标记为可缓存遇到原本不可缓存的响应时用它解锁s-maxage的作用容易被漏掉它只对共享缓存CDN、反向代理生效并且会覆盖max-age和Expires。所以你可以写成Cache-Control: max-age60, s-maxage3600——浏览器只缓存 1 分钟CDN 缓存 1 小时。这是内容分发里很常用的组合。private和no-store也容易混private是共享缓存不能存浏览器可以存no-store是谁都不能存。带用户信息的页面想省点流量用private比no-store更合适。ETag 和 Last-Modified 谁优先两者都是验证器validator但强度不同。RFC 9110 对强验证器的定义是A “strong validator” is representation metadata that changes value whenever a change occurs to the representation data that would be observable in the content of a 200 (OK) response to GET.弱验证器的定义则是内容每次变化时它不一定跟着变。ETag 默认是强验证器但如果不满足强验证器的全部条件必须用W/前缀标记为弱An entity tag can be either a weak or strong validator, with strong being the default. If an origin server provides an entity tag … does not satisfy all of the characteristics of a strong validator …, then the origin server MUST mark the entity tag as weak by prefixing its opaque value with “W/”.两类比较方式的差异RFC 9110 §8.8.3.2比较方式规则用在哪强比较两者都不是弱标记且值逐字符相等If-Match、If-Range弱比较值逐字符相等即可忽略W/标记If-None-Match这里有个反直觉的规定If-None-Match协商缓存用的那个走的是弱比较。规范原文A recipient MUST use the weak comparison function when comparing entity tags for If-None-Match.也就是说缓存验证这一环W/abc和abc被认为是匹配的。优先级问题有明确答案两者同时存在时If-None-Match赢If-Modified-Since被忽略。A recipient MUST ignore If-Modified-Since if the request contains an If-None-Match header field, the condition in If-None-Match is considered to be a more accurate replacement for the condition in If-Modified-Since.完整的求值顺序是If-Match→If-Unmodified-Since→If-None-Match→If-Modified-Since→If-Range。而If-Match和If-Unmodified-Since对缓存不适用它们用于并发写控制。那就是说ETag 更好、只用 ETag 就行也不对。规范对验证请求的要求是In most cases, both validators are generated in cache validation requests, even when entity tags are clearly superior, to allow old intermediaries that do not understand entity tag preconditions to respond appropriately.多数情况下两个都要发原因是链路上可能有只认Last-Modified的老中间层。这不是冗余是兼容性设计。Last-Modified 的问题出在精度为什么说它天生弱因为 HTTP 日期的时间分辨率是秒。规范明确点出了这个缺陷a representation’s modification time, if defined with only one-second resolution, might be a weak validator if it is possible for the representation to be modified twice during a single second and retrieved between those modifications.一秒内改两次、中间被访问一次Last-Modified就骗了你——客户端会认为内容没变继续用旧的。对高频发布的内容比如构建产物、动态页面这是个实打实的风险。而且Last-Modified的弱不是可选的规范对它的比较规则里写着A Last-Modified time, when used as a validator in a request, is implicitly weak unless it is possible to deduce that it is strong.所以ETag该配还是要配——不是因为ETag 总是更好这个笼统说法而是因为秒级精度确实不够用。304 响应有哪几条硬规定拿到 304 不等于什么都不用回。规范要求 304必须重复一些头The server generating a 304 response MUST generate any of the following header fields that would have been sent in a 200 (OK) response to the same request: Content-Location, Date, ETag, and Vary / Cache-Control and Expires还有一个容易忽略的规定304 不能带消息体也不能带 trailerA 304 response is terminated by the end of the header section; it cannot contain content or trailers.“必须重复Cache-Control和Expires这条有实际意义304 不只是告诉浏览器没变”它还在刷新缓存的新鲜期。如果 304 没带Cache-Control有些实现会不知道新的 max-age 该算多久。Vary被忽视的缓存命中杀手Vary决定一个缓存条目能匹配哪些请求。规范对它的定位是In other words, Vary expands the cache key required to match a new request to the stored cache entry.举个例子Vary: Accept-Encoding表示按 gzip 压缩的和不压缩的算两个不同条目。如果响应随客户端的Accept-Language变化就必须写Vary: Accept-Language否则会把中文用户和英文用户的缓存混在一起。最需要警惕的是Vary: *A stored response with a Vary header field value containing a member “*” always fails to match.永远不匹配等于把这个响应彻底踢出缓存。它有时候是刻意的比如就是不想被缓存但更多时候是配置出错导致的缓存完全失效、源站被打爆。F5 和 CtrlF5 到底做了什么这部分规范层面有定义浏览器层面只有模糊描述需要分开说。规范侧Fetch 标准定义了缓存的几种模式cache mode“reload”: Fetch behaves as if there is no HTTP cache on the way to the network. Ergo, it creates a normal request and updates the HTTP cache with the response. “no-cache”: Fetch creates a conditional request if there is a response in the HTTP cache and a normal request otherwise.对应到日常行为普通刷新走no-cache语义有缓存就发条件请求去验证强制刷新走reload语义完全绕开缓存。厂商文档侧Chrome 官方只区分三档Normal Reload / Hard Reload / Empty cache and hard reload并没有逐字把 F5、CtrlF5 映射到 Fetch 的枚举值。这个映射关系是社区总结出来的方向没错但严格来说不是官方口径。另外 RFC 8246 对 reload 也有一句话侧面印证了普通刷新会发条件请求Clients SHOULD NOT issue a conditional request during the response’s freshness lifetime (e.g., upon a reload) unless explicitly overridden by the user (e.g., a force reload).immutable 和 stale-while-revalidateimmutable常被当成Cache-Control的标准指令其实它不在 RFC 9111 里对 9111 全文检索 “immutable” 是 0 命中而是由RFC 8246单独定义的扩展When present in an HTTP response, the immutable Cache-Control extension indicates that the origin server will not update the representation of that resource during the freshness lifetime of the response. Clients SHOULD NOT issue a conditional request during the response’s freshness lifetime (e.g., upon a reload) unless explicitly overridden by the user (e.g., a force reload).它的价值在于干掉普通刷新时的条件请求——带 hash 的静态资源app.a3f9d2.js内容永远不会变刷新时去验证纯属浪费。加immutable就能让 F5 也直接吃缓存。stale-while-revalidate的语义是先返回旧内容同时后台发起验证更新。它在 Fetch 标准里被定义行为是If the HTTP cache contains a matching stale-while-revalidate response it will be returned, and a conditional network fetch will be made to update the entry好处是用户永远不等网络代价是可能返回一次过期内容。适合对一致性要求不高的列表页、非关键组件。一个可以直接抄的配置策略资源类型建议配置理由带 hash 的 JS/CSSmax-age31536000, immutable内容不变长期缓存图片、字体max-age604800 ETag内容稳定偶有替换HTML 入口no-cache必须每次最新但可省带宽接口数据可复用max-age60, s-maxage600浏览器短缓存、CDN 长缓存用户私有数据private, max-age0, must-revalidate可存但不许共享、必须验证敏感/一次性数据no-store不留任何痕迹三条工程原则入口文件用no-cache静态资源用长max-age hash敏感接口用no-store。这套组合能覆盖绝大多数站点。五个流传说法逐个对一下“no-cache就是不缓存”——错。可以存只是每次要验证。这是本篇最需要纠正的一条。“no-store和no-cache差不多”——两者一个禁止存储、一个允许存储但必须验证性质完全不同。“ETag 比 Last-Modified 总是更好”——不成立为总是。规范的立场是两个都发为了兼容老中间层ETag 的优势在于精度和可靠性不是可以替代。“协商缓存一定会发请求”——在常见情况下成立但有例外immutable会阻止普通刷新时的条件请求reload模式直接绕开缓存stale-while-revalidate是先返回旧的再后台验证。把它当铁律会在排查时误判。“强缓存命中不发请求”——这条是对的规范明确新鲜期内可直接复用。判断是否命中看 Network 面板有没有请求记录、Transfer Size 是不是 0。缓存指令的语义边界最容易被记串尤其是 no-cache 这类反直觉的名字。我现在的做法是把「指令—官方语义—常见误用」整理成一张对照表统一维护需要的时候用AI 图文同步一次推到几个平台留档集中核对的那几天靠发文额度提升不用排队发完再用批量 GEO 检测确认内容在 AI 搜索里的引用情况墨衍会员权益 有需要可以了解。常见问题Q为什么改了 Nginx 配置浏览器还是用旧缓存因为已经缓存下来的资源在你改配置之前就已经确定了有效期。修改响应头只影响新发起的请求。调试时用强制刷新或开 DevTools 的 “Disable cache”或者直接换 URL加查询参数绕过。Qmax-age和Expires同时存在用哪个用max-age。Expires是 HTTP/1.0 的产物需要服务端和客户端时钟同步Cache-Control存在时它会被忽略。现代配置里基本不需要写Expires只在兼容极老的客户端时才带上。QETag 是服务器自动生成的吗Nginx、Apache 等默认会基于文件的修改时间和大小生成一个 ETag但这个算法在多机部署时容易不一致——不同机器的 inode、mtime 可能有差异导致同一个文件在不同节点上 ETag 不同缓存反复失效。所以很多团队会关掉自动 ETag改用文件内容的 hash通常直接拼进文件名来生成。Q304 和 200 相比真的省流量吗省响应体但不省往返。304 依然是一次完整的请求-响应往返省下的是那部分可能很大的响应体。所以对大文件收益明显对小接口可能还不如把max-age设长一点直接命中强缓存。QCDN 上的缓存怎么和浏览器缓存配合用s-maxage区分Cache-Control: max-age60, s-maxage3600。浏览器只看max-age60 秒CDN 只看s-maxage3600 秒。这样既能保证用户拿到相对新的内容又能把回源压力降到很低。关于墨衍如果你也在多个平台发技术文章值得看看墨衍。三个最常用的权益——发文额度提升密集更新不再受限、批量 GEO 检测一次扫完全部文章的 AI 引用状态、AI 图文同步一稿多平台分发。点这里了解墨衍会员
阅读完成 · 觉得有帮助?
咨询建站