Go 的 http.ServeFile 不支持 HTTP 缓存头,生产环境应避免直接使用;推荐用 http.FileServer 配合 http.StripPrefix 实现带 ETag 和 Last-Modified 的缓存服务,或用 http.ServeContent 精确控制动态内容缓存。

Go 的 http.ServeFile 不支持 HTTP 缓存头,别直接用它上生产
它默认不写 Cache-Control、ETag 或 Last-Modified,浏览器每次都会发请求(哪怕文件没变),浪费带宽还拖慢首屏。真要缓存,得自己控制响应头或换更合适的机制。
- 静态资源(JS/CSS/图片)建议用
http.FileServer配合自定义http.Handler,而不是http.ServeFile -
http.ServeFile适合调试单个文件,比如http.ServeFile(w, r, "index.html"),但一加缓存需求就立刻失效 - 如果必须用
http.ServeFile,只能手动在调用前写头:w.Header().Set("Cache-Control", "public, max-age=3600"),但无法自动计算ETag或校验If-None-Match
用 http.FileServer + http.StripPrefix 实现带缓存的静态服务
这是 Go 标准库里最轻量又可控的方式:文件系统自动读取、支持 ETag 和 Last-Modified,还能插手响应头。
- 关键点是别直接传
http.Dir给http.ListenAndServe,而要包一层http.FileServer: fs := http.FileServer(http.Dir("./static")) http.Handle("/static/", http.StripPrefix("/static/", fs))- 这样访问
/static/main.js会映射到./static/main.js,且响应自带ETag和Cache-Control: public - 如需调整缓存策略,用中间件包装
fs,例如强制 1 小时缓存:http.Handle("/static/", http.StripPrefix("/static/", cacheHandler(fs))),其中cacheHandler是个自定义http.Handler,在serveHTTP里调w.Header().Set("Cache-Control", "public, max-age=3600")
手动设置 Cache-Control 时,max-age 和 immutable 别乱组合
immutable 是个容易被忽略但影响很大的指令:它告诉浏览器“这个资源永远不会变”,于是跳过所有 ETag / Last-Modified 校验。但 Go 默认不加它,你加了就得确保资源路径带哈希(比如 main.a1b2c3.js),否则更新后用户永远拿不到新版本。
- 安全的组合是:
Cache-Control: public, max-age=31536000, immutable—— 仅用于内容哈希命名的静态资源 - 开发环境慎用
immutable,本地改 JS 后刷新看不到效果,因为浏览器根本不发请求 - API 响应一般用
no-cache或no-store,别和静态资源策略混用;no-cache允许缓存,但强制校验(走 304),no-store才是彻底禁用缓存 - 注意
max-age=0≠no-cache:前者仍可能复用缓存(取决于其他头),后者明确要求校验
用 http.ServeContent 精确控制流式响应的缓存行为
当你不是读文件,而是从数据库、内存或远程服务动态生成内容(比如导出 PDF、拼接 SVG),又想支持断点续传和 304,就得绕过 FileServer,用 http.ServeContent。
立即学习“go语言免费学习笔记(深入)”;
- 它需要你提供
modtime(最后修改时间)、size(内容长度)和一个io.Reader,内部自动处理If-Modified-Since、If-None-Match和分块传输 - 常见错误:传错
modtime(比如用time.Now()),导致每次响应都算“新内容”,缓存失效 - 示例片段:
http.ServeContent(w, r, "report.pdf", modTime, reader)
,其中modTime应该是该报告实际生成/更新的时间戳,不是当前时间 - 注意:它不会自动加
Cache-Control,你得自己写:w.Header().Set("Cache-Control", "public, max-age=3600")


















