revel框架需通过路由接管静态资源并显式设置cache-control头,禁用http.fileserver直连;配置拦截器统一注入public, max-age=31536000, immutable,并结合resourceversion版本号实现url级缓存失效。

Revel 框架本身不自动给静态资源加缓存头,直接暴露 /public 下的文件(比如用 http.FileServer)时,Cache-Control 默认是空的,浏览器按启发式缓存(通常只有几分钟),根本达不到“长效”要求。必须通过 Revel 的路由机制接管静态资源,并显式设置响应头。
确保静态资源走 Revel 路由而非裸 FileServer
这是最关键的前置条件。如果在 app/init.go 里写了类似 http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("public/")))),那所有缓存控制逻辑都会被绕过——Revel 的 filter(包括自定义拦截器)完全不生效。
- 改用 Revel 原生静态路由:在
conf/routes中添加GET /public/*filepath controllers.Static.Serve("public") - 或在
app/init.go中注册revel.InterceptMethod(app.AddStaticHeaders, revel.BEFORE),但前提是请求必须经过 Revel 的 router,不能直连http.FileServer - 验证方式:启动后访问
/public/css/app.css,看响应头是否有Cache-Control;没有则说明没走 Revel 流程
用拦截器统一注入 Cache-Control 头
在 app/controllers/staticcache.go 中定义拦截器,对匹配 /public/ 路径的响应强制设置缓存策略:
func AddStaticHeaders(c *revel.Controller) revel.Result {
if strings.HasPrefix(c.Request.URL.Path, "/public/") {
c.Response.Out.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
}
return nil
}
-
max-age=31536000对应 1 年,适合带版本号的资源(如app.js?v=abc123) -
immutable告诉浏览器“这个 URL 永远不会变”,可避免反复验证,但仅在 Chrome/Firefox 支持,Safari 部分版本忽略 - 必须在
revel.InterceptMethod(AddStaticHeaders, revel.BEFORE)中注册,且放在其他可能提前返回的拦截器之后
配合资源版本化避免缓存击穿
只设长缓存不加版本号,上线新 CSS/JS 后用户会卡在旧文件里。Revel 没内置 fingerprinting,得靠配置驱动:
- 在
conf/app.conf中定义resourceVersion = ${GIT_COMMIT:dev}(构建时注入 commit SHA) - 写拦截器把该值注入模板上下文(参考知识库中
SetVersion示例) - 模板中写成
<script src="/public/js/app.js?v=%24%7BresourceVersion%7D"></script> - 这样每次部署 URL 实质变更,浏览器视为全新资源,无需清旧缓存
最容易被忽略的是:Nginx 或 CDN 层可能覆盖 Go 层的 Cache-Control。如果用了反向代理,务必检查它是否透传或重写了响应头——尤其当 curl -I 看到 Go 层设置了 max-age=31536000,但浏览器 Network 面板里显示的是 max-age=600,问题八成出在代理层。











