Gin默认不设Cache-Control头,导致浏览器和CDN无法缓存静态资源;需在路由层用中间件按路径前缀手动注入,如/static/下哈希文件设public, max-age=31536000,无哈希文件则设no-cache, must-revalidate,并确保中间件注册在r.Static()之前。

静态缓存不是加个 Cache-Control 头就完事——Gin 默认不设缓存头,浏览器反复拉 JS/CSS,CDN 也因缺失响应头跳过缓存;更麻烦的是,缓存策略得和前端构建产物生命周期对齐,否则用户永远拿不到新版。
为什么 r.Static() 返回的文件没缓存头?
Gin 的 Static 和 StaticFS 在 v1.9.1 之前默认不设置 Cache-Control,也不带 ETag,浏览器每次都会发完整请求。v1.9.1+ 虽补了 ETag,但依然不写缓存时长。
-
r.Static("/static", "./dist/static")只返回基础Content-Type,Cache-Control是空的 - 浏览器看到无缓存头,按默认策略(如 no-cache)处理,导致重复下载
- CDN 依赖
Cache-Control决定是否缓存、缓存多久;缺它,静态资源永远“就近”不了
如何给静态路径统一加 Cache-Control?
不能靠改 HTML 或前端构建配置——那是源头,而缓存行为由后端响应头最终决定。必须在 Gin 路由层拦截并注入。
- 用中间件判断路径前缀:
strings.HasPrefix(c.Request.URL.Path, "/static/") - 手动写头:
c.Header("Cache-Control", "public, max-age=31536000")(一年,适用于哈希命名的构建产物) - 注意顺序:中间件要放在
r.Static()之前注册,否则不生效 - 别用
c.Next()后再写头——Statichandler 已写完响应,header 会被忽略
示例:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
router.Use(func(c *gin.Context) {
if strings.HasPrefix(c.Request.URL.Path, "/static/") {
c.Header("Cache-Control", "public, max-age=31536000")
c.Header("Vary", "Accept-Encoding")
}
c.Next()
})
router.Static("/static", "./dist/static")
CDN 场景下,MIME 类型和压缩头怎么配?
CDN(如 Cloudflare、阿里云 CDN)会按响应头做内容分发决策:MIME 错误导致 JS 不执行,缺 Vary: Accept-Encoding 会让 gzip 版本被错误缓存为非压缩版。
-
.js返回text/javascript(不是application/javascript),部分老 CDN 会拒绝缓存 - 确保
gzip中间件开启前,前端构建产物已预压缩(如生成.js.gz),否则 Gin 动态压缩反而拖慢首字节 -
Vary: Accept-Encoding必须和Content-Encoding: gzip成对出现,否则 CDN 可能混存压缩/未压缩版本 - Windows 下路径分隔符(
\)可能导致os.Stat失败,影响 MIME 推断——统一用filepath.ToSlash()处理路径
生产环境要不要用 embed.FS 替代磁盘读取?
可以,但只在二进制打包阶段有意义:把 ./dist 打进可执行文件,避免部署时漏传静态目录。但它不解决缓存问题,反而让热更新失效。
-
embed.FS需 Go 1.16+,且必须用//go:embed dist/*声明 -
r.StaticFS("/static", http.FS(staticFS))仍需手动加缓存头,embed.FS不自动带ETag或Cache-Control - 若你用 CI 构建二进制,又希望 CDN 缓存,建议仍走磁盘 + CDN 回源模式,而非全量 embed
- 嵌入后无法用
c.File()直接服务根页——embed.FS不支持os.Stat,c.File会 panic,得换c.FileFromFS+ 显式判断路径
最易被忽略的一点:缓存时间必须和前端构建的文件名哈希策略匹配。如果 JS 文件是 app.a1b2c3.js,那 max-age=31536000 没问题;但如果是 app.js 无哈希,就得设成 max-age=0, must-revalidate,否则用户永远看不到更新。

















