我们线上有一次发布事故,改了一个接口的返回数据,结果用户那边半小时内拿到的全是旧内容。查了半天,最后发现是Nginx默认给接口加了Cache-Control: max-age=1800,前端同学没注意,直接踩进了HTTP缓存机制的坑里。从那以后我就觉得,缓存这个东西,不把强缓存和协商缓存的原理彻底搞明白,早晚会在线上给你上一课。
所以这篇文章想把HTTP缓存机制这件事从头到尾讲透。不管你是前端、后端还是运维,只要你的服务跑在HTTP上,这事就跟你有关。我会从浏览器和服务器之间的缓存约定讲起,把强缓存、协商缓存的字段、流程、优先级、配合方式全部拆开,最后给出一套可以直接抄走的配置方案和排障思路。
1. 先搞清楚:一次没有缓存的HTTP请求到底有多“亏”
1.1 一次完整请求往返的成本,比你想象的更高
很多人觉得,一次HTTP请求不就是“发出去、收回来”吗?能有多慢?但你看一下时间线就知道,在一个完全没有缓存的页面里,加载一张图片、一个JS文件,背后经历的环节包括:DNS解析、TCP三次握手、TLS握手(如果走HTTPS)、发送请求头、服务器处理、返回响应体。如果页面里有几十个静态资源,这些环节要重复几十遍。
我举个例子你就明白了。假设一个页面有30个静态资源,每个资源完整往返需要200ms,没有缓存的情况下光这些资源的网络耗时就是6秒。如果其中有10个资源能被强缓存命中,浏览器连请求都不发,直接从本地读,省掉的不只是200ms,而是这一整个资源的所有网络环节。这就是缓存的魅力——它不是把请求变快,而是让很多请求压根就不会发生。
1.2 缓存机制不是“不访问服务器”,而是分两档策略
很多人把HTTP缓存理解成“浏览器把资源存下来,下次直接用”,这个说法对了一半。实际上HTTP缓存分成两条路线:一条是强缓存,资源没过期就直接用本地的,服务器完全不知情;另一条是协商缓存,浏览器会带着资源的标识去问服务器“我这个副本还能不能用”,服务器说“可以用”,那就继续用本地的,服务器说“不行”,那就返回新资源。
这两条路线不是互斥的,而是先后关系。浏览器收到响应时,先看强缓存规则,如果命中,本地直接用,完全不发请求。如果强缓存没命中,比如过期了,那就进入协商缓存流程,发一个带条件的请求去问服务器。所以你可以把强缓存理解为“免检通道”,把协商缓存理解为“安检通道”,两者配合起来才是一套完整的HTTP缓存机制。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 强缓存:服务器允许你“免检入场”
2.1 Expires和Cache-Control:两个头字段的前世今生
强缓存的核心是两个响应头:Expires和Cache-Control。Expires是HTTP/1.0时代的产物,它给的是一个绝对时间,比如Expires: Thu, 01 Dec 2025 08:00:00 GMT,浏览器在这个时间之前直接用本地缓存。这个设计看起来很直观,但问题很大:服务器时间和用户本地时间可能不一致,用户改一下系统时间,缓存就废了。
所以HTTP/
