HTTP协议之缓存

发布时间:2026/8/4 8:57:38
HTTP协议之缓存 HTTP协议之缓存大家好今天我们来聊一个每个Web开发者都绕不开的话题——HTTP缓存。你可能有过这样的经历明明改了代码刷新页面却还是旧样式或者服务器压力大得不行但其实很多请求压根不需要打到后端。这些问题的答案都藏在HTTP协议的缓存机制里。### 为什么需要缓存想象一下你每天去食堂吃饭如果每次都要从菜园子现摘菜、现炒菜那得等到花儿都谢了。服务器也是一样如果每个请求都要去数据库查、去磁盘读、去CPU算那响应速度会非常慢而且服务器也扛不住。HTTP缓存就是给服务器准备的一个“保温饭盒”——把已经做好的“菜”响应数据先存起来下次有人点同样的“菜”直接端出来就行不用重新做。这样做的好处显而易见-减少网络延迟数据从本地或就近的缓存读取比从服务器拉取快得多-降低服务器负载大量重复请求在缓存层就被消化掉了-节省带宽成本尤其对于图片、视频等大文件效果立竿见影### 缓存是怎么工作的HTTP缓存的核心是响应头和请求头之间的“协商”。服务器通过响应头告诉浏览器“这份数据你可以存多久”浏览器在下次请求时通过请求头告诉服务器“我这里有份缓存你看还能不能用”。最常见的两个响应头是-Cache-Control现代缓存控制的核心可以设置max-age有效时间、no-cache使用前验证、no-store禁止缓存等-Expires老牌的过期时间指定一个具体的GMT时间优先级低于Cache-Control以及一个关键的验证机制-ETag给资源生成一个唯一标识类似指纹浏览器请求时带上If-None-Match服务器比对后返回304 Not Modified表示缓存可用-Last-Modified资源最后修改时间配合If-Modified-Since使用### 强缓存 vs 协商缓存这是两个必须搞清楚的概念强缓存浏览器直接使用本地缓存根本不发请求。只要Cache-Control: max-age3600且没超过1小时就直接用缓存。此时HTTP状态码显示为200 (from disk cache)或200 (from memory cache)。协商缓存浏览器会先发请求问服务器“我的缓存还能用吗”服务器通过ETag或Last-Modified判断如果资源没变返回304浏览器继续用缓存如果变了返回200和新资源。我画个简单的流程图浏览器发起请求 | v有强缓存且未过期 ---是--- 直接使用缓存不发请求 | 否 v有协商缓存ETag/Last-Modified | v发送请求带上If-None-Match/If-Modified-Since | v服务器判断资源是否变化 | ---未变化--- 返回304浏览器用缓存 | ---已变化--- 返回200新资源更新缓存### 代码示例Python Flask演示缓存我们先写一个简单的Flask应用来演示缓存头设置pythonfrom flask import Flask, request, make_responseimport timeapp Flask(__name__)app.route(/api/data)def get_data(): # 模拟一个耗时的数据计算 data {message: Hello, Cache!, timestamp: time.time()} # 创建响应对象 resp make_response(data) # 设置强缓存60秒内直接使用本地缓存 resp.headers[Cache-Control] public, max-age60 # 设置ETag用于协商缓存 etag fv1-{data[timestamp]} resp.headers[ETag] etag # 判断客户端是否发送了If-None-Match头 if request.headers.get(If-None-Match) etag: # 客户端缓存是最新的返回304 return , 304 return respapp.route(/api/no-cache)def no_cache(): data {message: This response should not be cached} resp make_response(data) # no-store表示禁止任何形式的缓存 resp.headers[Cache-Control] no-store return respif __name__ __main__: app.run(debugTrue, port5000)在这个示例中-/api/data设置了60秒的强缓存并提供了ETag用于协商缓存-/api/no-cache明确禁止缓存适合敏感数据### 代码示例Node.js实现缓存策略再看看Node.js中如何实现一个带缓存控制的静态文件服务javascriptconst http require(http);const fs require(fs);const path require(path);const crypto require(crypto);const server http.createServer((req, res) { const filePath path.join(__dirname, public, req.url / ? index.html : req.url); fs.readFile(filePath, (err, content) { if (err) { res.writeHead(404); res.end(File not found); return; } // 计算文件的MD5作为ETag const etag crypto.createHash(md5).update(content).digest(hex); // 设置缓存头 res.setHeader(Cache-Control, public, max-age86400); // 缓存1天 res.setHeader(ETag, etag); // 协商缓存如果客户端发送的ETag匹配返回304 if (req.headers[if-none-match] etag) { res.writeHead(304); res.end(); return; } // 设置Content-Type const ext path.extname(filePath); const mimeTypes { .html: text/html, .css: text/css, .js: application/javascript, .png: image/png, .jpg: image/jpeg }; res.setHeader(Content-Type, mimeTypes[ext] || text/plain); res.writeHead(200); res.end(content); });});server.listen(3000, () { console.log(Server running on port 3000);});这个Node.js服务器- 为每个文件生成MD5作为ETag- 设置max-age86400的强缓存- 处理If-None-Match头返回304让缓存生效### 实际开发中的缓存策略建议1.静态资源图片、CSS、JS设置较长的Cache-Control: max-age315360001年配合文件名hash如style.a1b2c3.css当文件变化时URL也变自然请求新资源 2.API接口根据数据实时性要求合理设置max-age或使用no-cache配合ETag做协商缓存3.HTML页面通常设置no-cache确保每次访问都能获取最新内容4.敏感数据用户信息、订单等坚决使用no-store禁止任何缓存### 常见坑点-Expires和Cache-Control同时存在时Cache-Control优先级更高现代浏览器几乎都遵循这一点-代理缓存Cache-Control: public允许CDN和代理缓存private只允许浏览器缓存-no-cache不是不缓存它表示“使用前必须验证”仍然会存储但每次要跟服务器确认-Pragma: no-cacheHTTP/1.0的遗留头用于兼容老浏览器现在基本不用了### 总结HTTP缓存是Web性能优化的基石理解并善用它能让你- 页面加载速度提升数倍- 服务器压力大幅下降- 用户体验显著改善核心要点就是强缓存Cache-Control: max-age让浏览器“懒得发请求”协商缓存ETag/304让浏览器“聪明地确认缓存是否有效”。实际开发中根据资源类型和业务需求灵活组合这些机制才能发挥最大威力。希望这篇文章能帮你理清HTTP缓存的脉络下次遇到“明明改了代码却不生效”的问题先检查一下缓存头说不定就是它在“捣鬼”呢

相关新闻