Go中Web缓存策略核心是合理设置Cache-Control响应头并配合ETag实现条件请求:静态资源用public,max-age=31536000,用户专属内容用private,max-age=600,实时接口用no-store或no-cache;ETag推荐弱校验格式W/"%x",基于内容哈希生成,配合If-None-Match实现304协商缓存;二者组合兼顾性能与一致性。

在 Go 语言中实现 Web 缓存策略,核心是合理使用 Cache-Control 响应头控制客户端/中间代理的缓存行为,并配合 ETag 实现条件请求(如 If-None-Match),避免重复传输未变更的内容。关键不在于“加缓存”,而在于“让缓存按预期工作”。
设置 Cache-Control 控制缓存生命周期
Cache-Control 是现代 HTTP 缓存最直接有效的指令。Go 的 http.ResponseWriter 可通过 Header().Set() 设置:
-
静态资源(JS/CSS/图片):用
public, max-age=31536000(1年),并确保文件名含哈希(如main.a1b2c3.js),这样可安全长期缓存; -
用户专属内容(如个人主页):用
private, max-age=600(10分钟),禁止 CDN 缓存,但允许浏览器缓存; -
实时性要求高的接口(如仪表盘数据):用
no-store或no-cache,前者完全禁用缓存,后者强制每次校验(仍会发请求,但可能返回 304)。
注意:max-age 优先级高于 Expires,无需再设后者;must-revalidate 可选添加,要求过期后必须校验,避免 stale 响应被无条件使用。
生成并响应 ETag 实现内容变更感知
ETag 是资源的唯一标识(通常为内容哈希),用于判断资源是否变更。Go 中推荐用 fmt.Sprintf("W/\"%x\"", sha256.Sum256([]byte(content))) 生成弱校验 ETag(带 W/ 前缀),更符合语义且兼容性好。
立即学习“go语言免费学习笔记(深入)”;
- 在处理请求时,先生成或查出当前资源的 ETag;
- 写入响应头:
w.Header().Set("ETag", etag); - 检查客户端是否携带
If-None-Match头,若匹配且资源未变,直接返回http.StatusNotModified(状态码 304),且不写响应体。
示例逻辑片段:
if match := r.Header.Get("If-None-Match"); match != "" && match == etag {w.WriteHeader(http.StatusNotModified)
return
}
组合使用:Cache-Control + ETag 构成完整缓存链路
单独用 Cache-Control 只能控制“缓存多久”,无法解决“缓存期间内容变了怎么办”;单独用 ETag 则每次都要发起请求(哪怕 304)。二者结合才是最佳实践:
- 首次响应:设置
Cache-Control: public, max-age=3600和ETag: W/"abc123"; - 1 小时内客户端再次请求:直接读本地缓存,不发网络请求;
- 1 小时后请求:自动带上
If-None-Match: W/"abc123",服务端比对 ETag,未变则返回 304,变则返回 200 + 新内容 + 新 ETag。
这种模式兼顾性能(减少请求数)与一致性(及时响应变更),是 RESTful API 和 SSR 页面的常用方案。
注意事项与易错点
-
动态内容慎用 public:含用户信息的响应若设
public,可能被 CDN 或代理缓存并错误共享给其他用户; - ETag 必须稳定可重现:不能依赖时间戳、随机数或数据库自增 ID;推荐基于内容本身(如 JSON 序列化后哈希)或版本字段生成;
-
不要忽略 Vary 头:若响应因
User-Agent或Accept-Encoding不同而异,需设置Vary: User-Agent, Accept-Encoding,否则缓存可能混淆; -
调试时禁用缓存:Chrome DevTools 的 “Disable cache” 仅影响浏览器,不模拟代理行为;建议用
curl -I查看真实响应头验证逻辑。










