gin 的 r.static() 默认不带 cache-control 是因为其底层 http.fileserver 仅设置 content-type 和 last-modified,不写缓存头,导致浏览器和 cdn 无法长效缓存;需用中间件在 r.static() 前拦截 /static/ 路径并手动添加 "public, max-age=31536000" 头,同时 index.html 应单独设 no-cache,windows 下还需统一路径分隔符并补全 mime 类型(如 woff2)。

为什么 Gin 的 Static 默认不带 Cache-Control?
Gin 的 r.Static() 内部用的是 http.FileServer,它只设置基础 Content-Type 和简单的 Last-Modified,完全不写 Cache-Control 头。浏览器拿到响应后按默认策略缓存(通常是短时间或无缓存),CDN 也因缺失明确指令跳过缓存 —— 这直接导致 JS/CSS 反复拉取、首屏变慢。
如何给 /static 路径统一加 long-term 缓存头?
最稳妥的做法是用中间件拦截所有匹配 /static/ 开头的请求,手动塞头。别依赖 StaticFS 自带的缓存逻辑,它不可控且不生效于所有场景。
- 必须放在
r.Static()之前注册,否则中间件收不到这些请求 - 路径判断要用
strings.HasPrefix(c.Request.URL.Path, "/static/"),注意结尾斜杠 -
Cache-Control值推荐"public, max-age=31536000"(1年),配合构建时文件名带 contenthash - 避免对
/static/目录本身(即结尾无文件名)设长缓存,否则目录列表可能被缓存
r.Use(func(c *gin.Context) {
if strings.HasPrefix(c.Request.URL.Path, "/static/") {
c.Header("Cache-Control", "public, max-age=31536000")
}
c.Next()
})
r.Static("/static", "./dist/static")
如何让 index.html 不被长缓存拖累?
index.html 不能和 JS/CSS 一起套 long-term 缓存,否则 HTML 更新了用户仍看到旧版入口。它需要短缓存 + 协商缓存兜底。
- 单独用
c.File("./dist/index.html")时,http.ServeFile不自动加ETag或Last-Modified - 手动触发协商缓存:在根路由 handler 中调用
c.Writer.WriteHeader(http.StatusOK)前先写头 - 更简单做法:把
index.html放进/static/下,用Cache-Control: no-cache单独覆盖
r.GET("/", func(c *gin.Context) {
c.Header("Cache-Control", "no-cache")
c.File("./dist/index.html")
})
Windows 下路径与 MIME 类型容易漏掉什么?
Windows 系统下路径分隔符混用(\ 和 /)会导致 os.Stat 失败,进而 404;同时 Gin 默认 MIME 映射不全,比如 .woff2 返回 text/plain,浏览器直接拒载字体。
- 静态目录路径务必用
filepath.Abs("./dist/static")构造,再传给r.Static() - 统一路径分隔符:
filepath.ToSlash()包一层,避免内部匹配失败 - MIME 类型需手动补全:
mime.AddType("font/woff2", ".woff2"),否则前端字体、SVG 图标可能失效
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











