Gin框架中gin.Static()默认不缓存,需通过gin.StaticOptions{MaxAge: N}设置秒级缓存时长,但仅支持Cache-Control无ETag/Last-Modified校验;可靠缓存失效依赖静态资源版本化文件名+短期强缓存+关键资源隔离。

gin.Static() 默认不缓存,浏览器每次请求都走服务端 —— 这是性能瓶颈的常见源头。直接设 MaxAge 能起效,但容易踩坑:缓存失效难控制、版本更新不生效、favicon.ico 被误缓存导致调试失败。
用 gin.StaticOptions{MaxAge: } 控制基础缓存时长
这是最轻量、最直接的方式,适用于 CSS/JS/图片等明确不变的资源:
-
MaxAge单位是秒,不是毫秒 —— 写成3600是 1 小时,写成3600000就错了 - 只对
gin.Static()挂载路径下的文件生效,不覆盖其他路由(比如/api/) - 它自动设置
Cache-Control: public, max-age=N,但不会加ETag或Last-Modified—— 浏览器无法做条件请求校验 - 示例:
r.Static("/static", "./dist/static", gin.StaticOptions{MaxAge: 604800})表示缓存 7 天
为什么 ETag 和 Last-Modified 不能靠 gin.Static() 自动提供
因为 gin.Static() 底层调用的是 http.ServeFile(),而它不读取文件元信息做校验;只有 http.ServeContent() 才支持 If-None-Match 和 If-Modified-Since 自动返回 304。
若你真需要 304 支持(比如静态资源偶尔会热更新),得自己封装 Handler:
- 用
os.Stat()获取ModTime()和Size() - 手动构造
ETag(如fmt.Sprintf("%d-%d", modTime.Unix(), size)) - 调用
http.ServeContent(w, r, name, modTime, file)替代http.ServeFile() - 注意:Gin 的
c.Writer不是标准http.ResponseWriter,需用c.Writer.HTTPResponseWriter向下转型(仅限 Gin v1.9+)
静态资源版本化才是缓存失效的可靠解法
靠 max-age 或 304 都解决不了“用户本地缓存了旧 JS,新 HTML 已上线,结果 JS 报错”的问题。唯一靠谱的是让文件名本身带变化:
立即学习“go语言免费学习笔记(深入)”;
- 构建时生成哈希文件名,比如
main.a8f5c1d.js,而不是main.js - 用 manifest 文件记录映射关系:
{"main.js": "main.a8f5c1d.js"} - Gin 渲染 HTML 时,从 manifest 中查出真实文件名:
c.HTML(200, "index.html", gin.H{"js": manifest["main.js"]}) - 模板里写:
<script src="/static/{{.js}}"></script> - 这样每次构建都会产生新 URL,浏览器强制拉新,旧缓存自然作废
别忽略 /favicon.ico 和调试资源的特殊性
它们常被 gin.Static() 一并捕获,但缓存太久会导致你改完图标却看不到效果,或者调试时 CSS 修改不生效:
- 单独为
/favicon.ico设置短缓存或禁用缓存:w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") - 开发环境建议关掉所有静态缓存:
MaxAge: 0或直接不用StaticOptions - 如果用了
statik或packr嵌入资源,注意它们默认不设缓存头,仍需手动加Cache-Control
max-age,而在如何让「缓存有效」和「缓存可废」同时成立 —— 版本化文件名 + 短期强缓存 + 关键资源隔离,这三者缺一不可。


















