Go net/http 怎样为静态文件设置缓存头
用 Go 的 net/http 提供前端静态文件时,真正决定浏览器和 CDN 缓存多久的不是 http.FileServer 这个名字,而是响应里的 Cache-Control。最稳妥的做法是在 FileServer 外面包一层 handler:带内容 hash 的 JS、CSS、字体可以长时间缓存,入口 HTML 则保留重新确认的机会。
官方文档:https://pkg.go.dev/net/http
这篇只讨论静态文件由 Go 直接提供的情况,不把缓存头、文件修改时间和 CDN 刷新混成一件事。下面的示例把策略写在服务端,资源文件仍交给标准库处理。
先把缓存策略分成两类
我在给一个前后端一体的小服务加缓存时,最先遇到的不是 API,而是“首页更新了,浏览器却还显示旧内容”。原因通常是把所有静态文件都设置成同一个长缓存。HTML 入口可能在每次发布时变化,而带 hash 的资源名本身已经承担了版本标识,二者的缓存策略应该分开。

| 资源 | 常见响应头 | 适用原因 |
|---|---|---|
| 带内容 hash 的 JS、CSS、字体 | public, max-age=31536000, immutable | 文件名变化就代表内容版本变化 |
| 没有 hash 的 HTML 入口 | no-cache 或较短的 max-age | 发布后希望客户端重新确认 |
| 用户私有或临时文件 | 按业务需要选择 private 或 no-store | 不应被共享缓存复用 |
no-cache 不是“不允许保存”,而是要求复用前向源站确认;如果响应内容根本不能落盘或不能被再次使用,才考虑 no-store。因此不要为了“保险”把所有静态资源都写成 no-store。
用 handler 包住 FileServer
http.FileServer 返回一个标准 handler,适合直接挂到路由上。缓存头属于应用对资源发布策略的判断,可以放在委托前完成;文件打开、内容类型、修改时间、范围请求等工作仍由标准库负责。
package main
import (
"log"
"net/http"
"strings"
)
func cachedStatic(root http.FileSystem) http.Handler {
files := http.FileServer(root)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 带 hash 的构建产物可以长缓存;入口 HTML 必须允许重新确认。
if strings.HasPrefix(r.URL.Path, "/assets/") {
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
} else {
w.Header().Set("Cache-Control", "no-cache")
}
// 继续交给 FileServer 处理路径、文件内容和标准 HTTP 响应。
files.ServeHTTP(w, r)
})
}
func main() {
// 静态目录下的 assets/ 与 index.html 由同一个服务提供。
handler := cachedStatic(http.Dir("./public"))
log.Fatal(http.ListenAndServe(":8080", handler))
}
这个包装器的关键点只有两个:先写 ResponseWriter.Header(),再调用底层 handler;同时把“资源是否带版本”落实为稳定的路径或命名约定。示例使用 /assets/ 作为约定,真实项目也可以在构建阶段给文件名加 hash 后统一放入该目录。

路径判断要和资源命名保持一致
最简单的判断是看 URL 前缀,但它只在路由约定稳定时可靠。不要仅凭“扩展名像图片”就给所有请求一年缓存:同一个后缀可能来自用户上传目录,也可能是需要马上替换的业务文件。
func staticPolicy(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 只对构建产物目录使用 immutable,避免误伤用户文件。
isAsset := strings.HasPrefix(r.URL.Path, "/assets/")
if isAsset {
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
} else if r.URL.Path == "/" || strings.HasSuffix(r.URL.Path, ".html") {
// HTML 入口允许缓存,但每次复用前都要回源确认。
w.Header().Set("Cache-Control", "no-cache")
}
// 不在包装层重新读取文件,保留下游 handler 的错误处理。
next.ServeHTTP(w, r)
})
}
如果服务前还有反向代理或 CDN,要把这套分类同步到它们的缓存规则中。源站发送了 public,并不等于 CDN 一定会按期望缓存;代理仍可能受到自身规则、响应状态和认证信息影响。
不要把缓存头和条件请求混为一谈
Go 的文件服务会根据文件信息参与常见的 HTTP 条件处理,文档中也列出了 ServeContent、ServeFile 和 FileServer 等接口。它们解决的是“内容怎样被提供、修改时间和范围怎样参与响应”;Cache-Control 解决的是“客户端和共享缓存能否保存、保存多久、是否要重新确认”。设置前者不会自动替你决定后者。
- Last-Modified / 条件请求:帮助客户端判断已有副本是否仍可复用,适合降低重复传输。
- Cache-Control:描述缓存权限和时间,例如
public、max-age、immutable。 - Range:服务大文件或媒体片段时表达部分内容请求,和缓存时长是不同维度。
所以,给 FileServer 加 Cache-Control 后,不能据此断言“以后一定不会请求源站”,也不能把一次 304 当成缓存策略已经正确。发布系统、代理层和资源命名仍要配合。
上线前按发布方式选择参数
可以把最后的决策压缩成三问:
- 文件名变化是否能可靠表示内容变化?能,就考虑长缓存和
immutable;不能,就使用短缓存或no-cache。 - 资源是否可能含用户私有内容?是,就不要直接使用面向共享缓存的
public。 - 中间是否有 CDN 或反向代理?有,就把源站头和代理规则一起设计,明确谁负责失效。
对多数 Go 前后端一体服务,一个可维护的默认值是:构建产物放进独立的 /assets/ 目录并带 hash,使用长缓存;HTML 入口使用 no-cache;不确定归属的上传文件不要套用静态资源策略。这样既能减少重复下载,也不至于让一次前端发布长期卡在旧入口上。
相关问题
设置了 max-age 后,为什么浏览器仍然请求源站?
可能是资源没有命中该 handler、请求带有认证信息、代理覆盖了响应头,或者客户端正在执行重新加载策略。先区分响应是否来自浏览器缓存、CDN 还是源站,再看最终响应头。
HTML 也能用一年缓存吗?
可以,但必须有可靠的版本化入口或发布失效机制。没有这些配套时,HTML 使用长缓存会让用户继续引用旧的 JS、CSS 资源,通常不如 no-cache 稳妥。
http.FileServer 会自动设置 Cache-Control 吗?
不要把它当作应用缓存策略配置器。它负责静态文件响应流程,缓存时长应由外层 handler、反向代理或 CDN 规则明确设置。
Go database/sql sql.Out 接收存储过程输出参数的绑定方法
- 上一篇
- Go database/sql sql.Out 接收存储过程输出参数的绑定方法
- 下一篇
- Go database/sql 怎样关闭 rows 并检查迭代错误
-
- Golang · Go教程 | 43分钟前 |
- Go database/sql 怎样关闭 rows 并检查迭代错误
- 195浏览 收藏
-
- Golang · Go教程 | 44分钟前 |
- Go database/sql sql.Out 接收存储过程输出参数的绑定方法
- 330浏览 收藏
-
- Golang · Go教程 | 50分钟前 | Go教程 · Go net/http 反向代理 httputil ReverseProxy NewSingleHostReverseProxy Rewrite SetURL
- Go net/http/httputil 怎样构造反向代理目标
- 435浏览 收藏
-
- Golang · Go教程 | 51分钟前 |
- Go cgo 怎样把 C 字符串转换为 Go 字符串
- 365浏览 收藏
-
- Golang · Go教程 | 52分钟前 |
- Go build constraints 怎样区分纯 Go 与 cgo 文件
- 381浏览 收藏
-
- Golang · Go教程 | 53分钟前 | go · Go Unsafe go:linkname 链接器
- Go linkname 使用前需要考虑哪些边界
- 177浏览 收藏
-
- Golang · Go教程 | 58分钟前 |
- Go toolchain 怎样用 GOTOOLCHAIN=local 固定版本
- 422浏览 收藏
-
- Golang · Go教程 | 58分钟前 | Go教程 · 模块管理 · Go 离线构建 go.work go mod vendor vendor/modules.txt go work vendor Go工作区
- Go work vendor 离线构建多模块工作区的依赖落盘方案
- 374浏览 收藏
-
- Golang · Go教程 | 1小时前 | 模块 · go · 依赖管理 go.mod Go Modules retract
- Go modules 怎样用 retract 标记不可用版本
- 483浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go runtime/trace.NewTask 怎样跨 goroutine 跟踪一次逻辑操作
- 266浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · Go Modules Go依赖管理 go mod graph
- Go modules 怎样用 go mod graph 查依赖关系
- 388浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go pprof 怎样区分 CPU 样本与墙钟时间
- 197浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 849次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 852次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 933次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 909次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 736次使用
-
- Go map 并发写 panic 怎么办:从共享 map 到可控写入路径
- 2026-06-30 123浏览
-
- Golang实现HTTP编程请求和响应
- 2022-12-28 101浏览
-
- golangNewRequest/gorequest实现http请求的示例代码
- 2023-01-24 343浏览
-
- 一文详解Golang中net/http包的实现原理
- 2022-12-29 419浏览
-
- go语言中的defer关键字
- 2023-02-17 150浏览

