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 是现代 HTTP 缓存最直接有效的指令。Go 的 http.ResponseWriter 可通过 Header().Set() 设置:
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 是资源的唯一标识(通常为内容哈希),用于判断资源是否变更。Go 中推荐用 fmt.Sprintf("W/\"%x\"", sha256.Sum256([]byte(content))) 生成弱校验 ETag(带 W/ 前缀),更符合语义且兼容性好。
立即学习“go语言免费学习笔记(深入)”;
w.Header().Set("ETag", etag);If-None-Match 头,若匹配且资源未变,直接返回 http.StatusNotModified(状态码 304),且不写响应体。示例逻辑片段:
if match := r.Header.Get("If-None-Match"); match != "" && match == etag {单独用 Cache-Control 只能控制“缓存多久”,无法解决“缓存期间内容变了怎么办”;单独用 ETag 则每次都要发起请求(哪怕 304)。二者结合才是最佳实践:
Cache-Control: public, max-age=3600 和 ETag: W/"abc123";If-None-Match: W/"abc123",服务端比对 ETag,未变则返回 304,变则返回 200 + 新内容 + 新 ETag。这种模式兼顾性能(减少请求数)与一致性(及时响应变更),是 RESTful API 和 SSR 页面的常用方案。
public,可能被 CDN 或代理缓存并错误共享给其他用户;User-Agent 或 Accept-Encoding 不同而异,需设置 Vary: User-Agent, Accept-Encoding,否则缓存可能混淆;curl -I 查看真实响应头验证逻辑。