浏览器HTTP缓存头怎么配?让回头客秒开又不犯改了不更新的事故

浏览器HTTP缓存头怎么配?让回头客秒开又不犯改了不更新的事故
张文保 25 分钟阅读 1,029 阅读
本文目录
  1. 浏览器到底把什么东西缓存了,又凭什么缓?
  2. Cache-Control这个头怎么读,常见指令都管什么?
  3. no-cache和no-store到底差在哪?
  4. Expires和Cache-Control是什么关系,过时了吗?
  5. 缓存过期后怎么不重复下载?ETag和Last-Modified怎么省流量?
  6. 这套缓存头怎么配才能既快又不出“改了不更新”的事故?
  7. 缓存头配错的翻车现场,以及它和SEO的关系
  8. 常见问题解答
  9. no-cache是不是就是“不缓存”,和no-store一样吗?
  10. Cache-Control和Expires都能设缓存时间,该用哪个?
  11. ETag和Last-Modified有什么区别,要不要都配?
  12. 为什么我更新了网站,有些用户看到的还是旧页面?
  13. 浏览器缓存配好了对SEO有帮助吗?
  14. 权威参考资料

同一个访客第二次打开你的站,该不该让浏览器重新下载那堆图片、CSS、JS?这事不归运气管,归HTTP缓存头管。保哥这篇把浏览器缓存讲透:强缓存和协商缓存到底差在哪、Cache-Control那一串指令分别管什么、最容易搞混的no-cache和no-store、Expires过时没、ETag和Last-Modified怎么用一个304省下整次下载,最后给一套“静态资源缓一年、HTML实时更新”既快又不翻车的配置策略。看完你就能让回头客的页面秒开,还不犯“改了代码用户看到的还是旧的”这种事故。

做独立站、跑外贸站的朋友,都想让页面打开更快。优化的招数很多,但有一档常被忽略、收益又极大的,就是浏览器缓存。道理很朴素:一个老访客今天来过、明天又来,你站上那些logo、CSS、字体、JS根本没变,凭什么让他的浏览器再下载一遍?让浏览器把这些东西存在本地,下次直接从硬盘读,页面几乎瞬间出来,服务器也少扛一份流量。这笔买卖,本该稳赚。

可问题是,浏览器怎么知道哪些能缓、缓多久、什么时候该去服务器问一句“变了没”?答案全藏在服务器响应时带的那几个缓存头里。保哥之前写TTFB多层缓存优化时把浏览器缓存列为离用户最近的一层,这篇就把这一层单独拎出来讲透——Cache-Control、Expires、ETag、Last-Modified这几个头到底怎么读、怎么配,才能既榨出速度,又不踩“改了不更新”的雷。

浏览器到底把什么东西缓存了,又凭什么缓?

先建立个整体认识。浏览器缓存的对象,主要是页面上那些静态资源:CSS样式表、JS脚本、图片、字体、图标等。这些东西体积不小、又长期不变,是缓存的最佳人选。至于HTML页面本身缓不缓、怎么缓,是另一档讲究,后面专门说。

那浏览器凭什么决定缓不缓、缓多久?凭服务器在响应头里下的指令。你访问一个资源,服务器返回内容的同时,会在HTTP响应头里带上Cache-Control、ETag这类字段,等于告诉浏览器:“这个文件你可以存一年”“这个每次用前来问我一下”。浏览器是个听话的执行者,照着这些指令办事。所以缓存策略的主动权在服务器、在你手里,配好这些头,就是在指挥每一个访客的浏览器怎么缓存你的站。

浏览器缓存分两大类机制,这是理解后面所有内容的总纲,必须先拎清楚:

  • 强缓存(强制缓存)。服务器说“这文件X秒内有效”,那么在这段时间里,浏览器再要用这个资源,压根不联系服务器,直接从本地缓存拿,连一个请求都不发。这是最快的——零网络往返。Cache-Control的max-age、Expires管的就是它。
  • 协商缓存(对比缓存)。强缓存过期后,浏览器也不是傻乎乎重新下载,而是带着一个“标识”去问服务器:我手上这份还能用吗?如果服务器说“没变”,回一个轻飘飘的304状态码(不带文件内容),浏览器继续用本地那份;只有真变了,才返回新文件。ETag、Last-Modified管的就是这一步。

把这两层串起来看,一个资源的完整命运是:第一次访问,老老实实下载,同时服务器告诉浏览器缓存规则;在强缓存有效期内,再要用就直接读本地、零请求;有效期过了,进入协商缓存,问一句服务器变没变,没变就回304接着用、变了才重新下。强缓存省的是整个请求,协商缓存省的是文件主体的传输,一层比一层退而求其次,但都比无脑重下强得多。下面就按Cache-Control、Expires、ETag的顺序,一个个拆开。

Cache-Control这个头怎么读,常见指令都管什么?

Cache-Control是现代HTTP缓存的核心,强缓存、协商缓存的行为很大程度上都由它的各种指令拼出来。它长这样,一个头里可以塞多条指令,逗号隔开:

Cache-Control: public, max-age=31536000, immutable
Cache-Control: no-cache
Cache-Control: private, max-age=0, must-revalidate

把常用的指令逐个讲清楚,你就能读懂、也能写出大部分缓存策略了:

  • max-age=秒数。最重要的一条,规定资源的“新鲜期”有多长。比如max-age=3600就是“一小时内算新鲜,浏览器直接用本地的,别来烦我”。31536000是一年的秒数,常用来缓那些永不变的静态资源。
  • no-cache。名字极具误导性——它不是“不缓存”,而是“可以缓存,但每次用之前必须先去服务器验一下还新不新”。也就是强制走协商缓存。下一节专门掰它和no-store。
  • no-store。这才是真正的“一点都别存”,浏览器和中间缓存都不许保存这个响应,每次都得完整重新请求。用于绝对敏感、绝对不能留痕的内容,比如含隐私的接口响应。
  • public。表示这个响应可以被任何缓存存储,包括CDN、代理这种“共享缓存”。
  • private。只允许存在用户自己的浏览器(私有缓存)里,CDN和代理不许缓。带个性化信息、登录态的页面要用它,免得A用户的私人页面被CDN缓了、发给了B用户。
  • immutable。告诉浏览器“这文件在有效期内绝对不会变”,于是连用户按刷新时本会触发的那次校验都省了,进一步减少请求。配长max-age用在带指纹的静态资源上效果最好。
  • must-revalidate。资源一旦过期,必须去服务器校验,不许在服务器联系不上时拿过期的凑合用。对正确性要求高的内容加这条更稳。
  • s-maxage=秒数。专门给共享缓存(CDN/代理)设的新鲜期,会覆盖max-age对它们的效果。这让你能给浏览器和CDN设不同的缓存时长。

这些指令是可以组合的,组合出来才是完整策略。比如 public, max-age=31536000, immutable 的意思是“谁都能缓、缓一年、期间绝不变”,这是给带哈希指纹的静态资源用的黄金配置;而 private, no-cache 是“只存浏览器、每次用前必验”,适合那种要保证拿到最新、又不想每次都重传全文的动态内容。会拼这几条,缓存策略就掌握大半了。

no-cache和no-store到底差在哪?

这俩是Cache-Control里最容易搞混、也最容易配错的一对,保哥单开一节讲,因为配错代价不小。

先记结论:no-cache是“存,但每次用前都要问”;no-store是“根本不存”。它俩差着十万八千里。

no-cache 这个名字坑了无数人。直觉上一看“no cache”,以为是“不缓存”,其实完全不是。它的真实含义是:浏览器可以把这个响应缓存下来,但在每次要用它之前,必须先向服务器发个校验请求,确认这份缓存还是最新的才能用。如果服务器说没变(回304),浏览器就用本地缓存,省下了重新传输文件的开销;如果变了,才下载新的。所以no-cache走的是协商缓存,它省流量(命中时只传一个304),但不省请求(每次都得问一下)。

no-store 才是字面意义上的“别缓存”。它命令浏览器和所有中间缓存:这个响应一个字节都别存,每次需要都得老老实实从服务器完整下载。它最彻底、最安全,但也最费——没有任何缓存收益。它的正确用武之地是绝对不能被缓存的敏感数据,比如银行交易页、含个人隐私的接口数据,存下来就有泄露风险的那种。

用错的后果很现实。本该用no-cache(每次验一下、变了才更新)的页面,错配成no-store,等于把缓存收益全扔了,每次全量重下,白白增加服务器压力和加载时间;反过来,本该no-store的敏感数据错配成no-cache甚至max-age,那就可能把不该留的东西留在了缓存里,是安全隐患。记牢这一句:要“实时但省流量”用no-cache,要“绝对不留”用no-store,别再被名字误导。

Expires和Cache-Control是什么关系,过时了吗?

你抓包看响应头时,可能还会见到一个 Expires 头,它和Cache-Control看着功能重叠,关系得理清楚。

Expires是HTTP/1.0时代的老头,它的值是一个绝对时间点,比如:

Expires: Wed, 21 Oct 2026 07:28:00 GMT

意思是“这个资源到这个时刻之前都算新鲜”。它干的活和Cache-Control的max-age类似,都是定强缓存的有效期,区别在于:Expires用绝对时间,max-age用相对秒数。

绝对时间有个天生的毛病——它依赖客户端的本地时钟。要是用户电脑的时间不准(这事比你想的常见),Expires的判断就会出错,该过期的没过期、或者反过来。而max-age是“从拿到响应起算多少秒”,是相对的,不受客户端时钟漂移影响,更可靠。

所以现在的标准答案是:优先用Cache-Control的max-age,Expires留着只是为了兼容极老的客户端。而且按规范,当Cache-Control的max-age和Expires同时存在时,max-age说了算,Expires被忽略。你平时配缓存,盯着Cache-Control配就行,Expires知道它是怎么回事、看到别懵就够了,不用专门去设。这也是为什么保哥说Cache-Control是现代缓存的核心——它把老的Expires该干的活更可靠地接管了。

缓存过期后怎么不重复下载?ETag和Last-Modified怎么省流量?

强缓存有效期内爽是爽,可总有过期的时候。过期了难道就只能重新下载整个文件?不是。这正是协商缓存登场的地方,主角是ETag和Last-Modified这对“验证器”。

先说Last-Modified,最直观。服务器返回资源时,带上这个文件的最后修改时间:

Last-Modified: Tue, 22 Feb 2026 20:20:20 GMT

浏览器把这个时间记下来。等强缓存过期、要再用这个资源时,浏览器发请求,带上一个 If-Modified-Since 头,值就是上次记下的那个时间,等于问服务器:“我这份是这个时间的,之后改过没?”服务器一比对:没改过,回一个 304 Not Modified,不带任何文件内容;改过了,才返回200加新文件。命中304时,省下的是整个文件主体的传输,只走了一个几乎没有体积的状态响应,流量收益巨大。

但Last-Modified有几个软肋:它的精度只到秒,一秒内多次修改它分辨不出来;有些文件内容没变、只是被重新生成或触碰了一下,修改时间变了它就误判成“变了”;分布式部署时多台服务器的文件时间还可能对不齐。为了解决这些,HTTP又设计了更精确的 ETag

ETag(实体标签)是服务器给资源算出的一个版本标识,可以理解成内容的指纹,内容一变指纹就变:

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

它的协商流程和Last-Modified一个套路,只是换了字段:浏览器再次请求时带上 If-None-Match 头,值是上次拿到的ETag,问服务器“我这份指纹是这个,跟你现在的一样吗?”一样就回304接着用,不一样才下发新内容。因为比的是内容指纹而不是时间,ETag比Last-Modified准得多,不会因为“时间变了内容没变”而误判。

这里还有个细节值得知道:ETag分强验证器和弱验证器。强ETag要求字节级完全一致,写法就是普通的引号包起来;弱ETag前面带个 W/ 前缀,表示“语义上等价就行,不要求字节完全相同”,更容易生成但精度略松。日常理解到“ETag是内容指纹、用于协商缓存”这一层,绝大多数场景就够用了。

当ETag和Last-Modified同时存在时,ETag优先级更高,服务器会以ETag的比对结果为准。实际中两个都带上也无妨,给不同能力的环境留个兜底。记住协商缓存的价值:它不能让你零请求(还得问一句),但能把“问完发现没变”的情况从一次完整下载压缩成一个304空响应,对大文件尤其划算。

这套缓存头怎么配才能既快又不出“改了不更新”的事故?

原理都懂了,落到实操:到底该怎么配,才能又快又不翻车?这里有个经典矛盾——缓存时间设长了快,但你更新了文件用户看到的还是旧的;设短了更新及时,但缓存收益又小了。怎么破?

业界的标准答案是按资源类型分而治之,核心是区分“带指纹的静态资源”和“HTML入口文件”,两套截然不同的策略:

  • 带哈希指纹的静态资源(CSS、JS、图片),用超长强缓存。现在的前端构建工具打包时,会给文件名加上一段内容哈希,比如 app.3f8a9c.js。内容一变,哈希就变,文件名也跟着变,等于变成了一个全新的URL。所以这类文件可以放心配 Cache-Control: public, max-age=31536000, immutable——缓一年、绝不校验。因为它永远不会“原地更新”,要更新就是换了个新文件名,浏览器自然会去请求新的那个。
  • HTML入口文件,用no-cache或极短缓存。HTML是整个页面的入口,它里面引用着那些带哈希的资源。HTML绝不能长缓,否则你发布了新版本、引用了新哈希的资源,用户的浏览器还拿着旧HTML、引用着旧资源,更新就不生效了。所以HTML通常配 Cache-Control: no-cache(每次验一下,靠ETag/304省流量)或者很短的max-age,保证用户总能尽快拿到指向最新资源的入口。

这套组合的精妙之处在于:用文件名指纹(cache busting,缓存击穿/破缓存)这个机制,把“长期缓存”和“及时更新”这对矛盾化解了。静态资源享受一年长缓存带来的极致速度,更新时换文件名让浏览器自然获取新版;HTML这个小小的入口不长缓,保证它永远指向最新的资源清单。两头都顾上了,这就是为什么现代网站既能秒开回头客、又不会出“改了不更新”的事故。

那不带指纹、又会更新的文件怎么办(比如你手动传的一张产品图,文件名固定)?折中配个中等时长的max-age,再配合协商缓存兜底——过期后靠ETag校验,没变就304,变了才重下。或者更新图片时主动改文件名/加版本号查询参数,手动制造cache busting。原则始终是:能靠换名实现长缓存的尽量长缓,必须固定名又会变的就上协商缓存做兜底。

实在拿不准某个文件该缓多久,宁可设短一点配上协商缓存,也别贸然设超长强缓存——短了无非多发几个304校验请求,损失有限;长了一旦要更新就只能干等过期或求用户清缓存,那才是真难受。缓存策略的安全垫,永远是协商缓存这道兜底。

那怎么确认缓存到底配没配对、有没有生效?打开浏览器开发者工具的“网络(Network)”面板,刷新页面,看每个资源那一行的“大小(Size)”列,状态就一清二楚了。

如果显示的是 from disk cache(从磁盘缓存)from memory cache(从内存缓存),说明命中了强缓存,连请求都没发,最理想;如果显示 304 状态码,说明走了协商缓存,发了校验请求但没重传文件主体,也不错;如果显示实打实的文件大小、状态码200,那就是又重新下载了一遍,说明没命中缓存。点开某个资源还能看到它的完整响应头,Cache-Control、ETag、Last-Modified各配成了什么一目了然,对照预期就知道配对没配对。

配完缓存务必这样抓一眼实际行为,别只在服务器配置里想当然——配了不等于生效,nginx、Apache、CDN任何一层覆盖了你的设置都可能让结果和预期不符。尤其要留意“硬刷新”和“普通访问”的差别:你按Ctrl+F5强制刷新时浏览器会绕开强缓存重新请求,看到的不是真实的回头客体验,想验证缓存效果得用普通的二次访问,或者干脆在隐身窗口里走一遍正常流程。

保哥举个真实排查。有个外贸站老板抱怨“回头客打开还是慢”,保哥让他开Network面板一看,全站几十个图片、JS每次访问都是200、实打实重下——压根没配缓存头。给静态资源加上一年的强缓存加文件名指纹后,再看面板,第二次访问那些资源全变成了from disk cache,页面肉眼可见地秒开。这种站太多了,不是优化不动,是连白送的缓存都没领。缓存头是那种“配置存在与否”就能拉开巨大差距的优化,先确认配了没,再谈配得好不好。

还要提醒一句,缓存头不只浏览器在看,CDN和反向代理也照着它缓。如果你站前面挂了 Cloudflare这类CDN,缓存头会同时指挥CDN的边缘缓存,这时public/private、s-maxage这些区分共享缓存的指令就格外重要——带登录态的页面务必private,否则可能被CDN缓存后串给别的用户,那就是事故了。浏览器缓存和CDN缓存用的是同一套头,理解了这套头,两层一起就都拿捏了。

缓存头配错的翻车现场,以及它和SEO的关系

最后,保哥把实操里最常见的翻车场景和容易忽略的SEO关联讲一讲。

翻车一:HTML也配了长max-age。头号事故。你发布了新版网站,可一部分用户死活看到的还是旧页面,刷新都没用——因为他们的浏览器把旧HTML强缓存住了,在过期前根本不来服务器。HTML必须no-cache或短缓存,切记。翻车二:no-cache当成了no-store。本想让页面实时更新又省流量,结果配了no-store,每次全量重下,性能白白损失。翻车三:带登录态的页面配了public。被CDN缓存后,一个用户的私人页面发给了另一个用户,隐私事故,个性化内容务必private。

翻车四:静态资源不加指纹却配超长缓存。文件名固定又缓一年,你更新了用户一年内都看不到,只能让用户清缓存——极差的体验。要长缓存就一定配合文件名指纹。翻车五:完全不配缓存头。不配的话浏览器只能用一套保守的默认启发式,缓存收益大打折扣,等于把白送的速度扔了。再小的站,给静态资源配上缓存头都是稳赚。

缓存头和SEO也有实打实的关系,做站的更该重视。其一,页面速度是Google的排名信号,缓存配好了,回头访问和资源加载更快,对Core Web Vitals(尤其LCP)有正面帮助,体验分上去了,排名也受益。

其二,影响抓取预算。Googlebot抓取时也尊重缓存语义,合理的缓存能减少对没变资源的重复抓取,把宝贵的抓取预算留给真正需要被发现的新内容——这一点保哥在讲服务端缓存与抓取效率时也反复强调过。一前一后两层缓存(浏览器这层管访客体验、服务端那层管动态生成),加上 OPcache砍编译,整条性能链路才算配齐。

说到底,浏览器缓存头是那种“配一次、长期收益”的优化:花点时间把静态资源配成长缓存加指纹、把HTML配成no-cache、把敏感内容配成no-store,回头客的页面就能秒开,服务器也轻松,还不犯“改了不更新”的低级事故。它不烧钱、不伤筋动骨,就是几行响应头配置的事,却是页面速度优化里性价比最高的一档,做独立站、外贸站的没有理由不把它配明白。配明白这一层,再往上叠CDN、往下叠服务端缓存,整个站的性能才算真正立住了。

常见问题解答

no-cache是不是就是“不缓存”,和no-store一样吗?

完全不一样,这是Cache-Control里最坑人的误解。no-cache的真实含义是“可以缓存,但每次用之前必须先去服务器验一下还新不新”,也就是强制走协商缓存——命中时服务器回304,浏览器用本地那份,省下了重新传输文件的流量,但每次都要发一个校验请求。而no-store才是字面意义的“一点都别存”,浏览器和所有中间缓存都不许保存这个响应,每次需要都得从服务器完整重新下载,没有任何缓存收益。两者用途也不同:要“实时更新又省流量”用no-cache(比如HTML入口);要“绝对不留痕”用no-store(比如含隐私的敏感数据)。配错代价不小——把该no-cache的配成no-store会白白损失性能,反之则可能造成敏感信息泄露。务必记牢这个区别。

Cache-Control和Expires都能设缓存时间,该用哪个?

优先用Cache-Control的max-age,Expires只为兼容极老的客户端保留。两者都是定强缓存有效期的,核心区别在于:Expires用的是绝对时间点(比如“到2026年某月某日某时之前有效”),而max-age用的是相对秒数(“从拿到响应起算多少秒内有效”)。绝对时间有个天生缺陷——它依赖客户端本地时钟,用户电脑时间不准时判断就会出错;而相对秒数不受客户端时钟漂移影响,更可靠。而且按HTTP规范,当max-age和Expires同时出现时,max-age优先、Expires被忽略。所以日常配缓存盯着Cache-Control配就够了,Expires知道是什么、看到别懵即可,不用专门设。Cache-Control被称为现代缓存核心,正因它更可靠地接管了老Expires的活。

ETag和Last-Modified有什么区别,要不要都配?

两者都是协商缓存的“验证器”,作用是让缓存过期后不必重下整个文件、只需校验一下。Last-Modified带的是文件最后修改时间,浏览器下次用If-Modified-Since问服务器“这之后改过没”,没改回304。它直观但有软肋:精度只到秒、内容没变但被重新生成时会误判、多服务器时间还可能对不齐。ETag是服务器给资源算的版本标识,相当于内容指纹,浏览器用If-None-Match比对指纹,内容没变就回304。因为比的是内容而非时间,ETag比Last-Modified准得多,不会“时间变了内容没变”就误判。两者同时存在时ETag优先。实际中可以都配,给不同环境留兜底,但ETag是更可靠的那个。理解到“ETag是内容指纹、用于协商缓存省流量”这一层,日常就够用了。

为什么我更新了网站,有些用户看到的还是旧页面?

九成是HTML被配了过长的强缓存。HTML是页面入口,里面引用着各种CSS、JS、图片资源。如果你给HTML配了长max-age,那么在缓存过期前,用户的浏览器根本不来服务器、直接用本地那份旧HTML,自然引用的还是旧资源,你发布的新版就不生效,刷新都没用(除非强制刷新)。正确做法是:HTML入口配no-cache或很短的max-age,保证用户总能尽快拿到指向最新资源清单的入口;而那些CSS、JS静态资源配长缓存加文件名哈希指纹,更新时哈希变、文件名变,等于新URL,浏览器自然会去请求新的。这套“HTML不长缓、静态资源靠换名长缓”的组合,正是既快又不出更新事故的关键。如果你的静态资源文件名固定又配了长缓存,更新后用户也会看到旧的,得改文件名或加版本号。

浏览器缓存配好了对SEO有帮助吗?

有实打实的帮助,主要体现在两方面。其一,页面速度是Google公开的排名信号之一,缓存配好后回头访问和资源加载更快,对Core Web Vitals(特别是衡量主内容加载的LCP)有正面作用,体验分上去排名也受益。其二,影响抓取预算——Googlebot抓取时也尊重缓存语义,合理的缓存能减少对没变资源的重复抓取,把有限的抓取预算省下来留给真正需要被发现和收录的新内容,这对内容量大的站尤其重要。需要说明的是,缓存优化不是直接的排名魔法,它是通过“更快的体验”和“更高效的抓取”间接利好SEO。把浏览器缓存这层和服务端缓存配合起来,性能和抓取效率双双改善,是做站值得投入的基础功,性价比很高。

权威参考资料

FAQPage + Article AI 引用友好版

TL;DR · 60–80 字摘要 · 适用 ChatGPT / Perplexity / Gemini / 文心 引用

老访客第二次打开你的站,那堆CSS、JS、图片该不该重新下载?全归HTTP缓存头管。保哥讲透强缓存与协商缓存、Cache-Control各指令、no-cache与no-store之别、ETag怎么用304省流量。

关键实体 · Key Entities

  • 性能优化
  • HTTP缓存
  • Cache-Control
  • 浏览器缓存
  • 缓存与CDN

引用元数据 · Citation Metadata

title:       浏览器HTTP缓存头怎么配?让回头客秒开又不犯改了不更新的事故
author:      张文保 (Paul Zhang) — PatPat SEO 经理
url:         https://zhangwenbao.com/http-browser-cache-control-etag-expires-cache-headers.html
published:   2026-01-26
modified:    2026-01-26
source-type: First-hand expert commentary
language:    zh-CN
license:     CC BY-NC-SA 4.0 (要求保留原文链接与作者归属)
分享到
标签
版权声明

本文标题:《浏览器HTTP缓存头怎么配?让回头客秒开又不犯改了不更新的事故》

本文链接:https://zhangwenbao.com/http-browser-cache-control-etag-expires-cache-headers.html

版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交