Echo 的 e.Static() 不设置强缓存头,浏览器默认走协商缓存;需通过 Nginx 配置 expires 或 add_header 注入 Cache-Control,或改用 Echo 手动路由+显式设置响应头才能实现强缓存。

Echo 本身不控制浏览器强缓存,必须由反向代理(如 Nginx)或 HTTP 响应头显式设置。直接在 Echo 中调用 e.Static() 或 c.File() 只能返回文件,但默认不带 Cache-Control: max-age=31536000 或 Expires 这类强缓存头——浏览器因此会走协商缓存(If-None-Match / If-Modified-Since),达不到“强缓存跳过请求”的效果。
为什么 e.Static("/static", "public") 不够用
它只是把 /static/js/app.js 映射到本地 public/js/app.js 并返回,响应头里只有基础的 Content-Type 和 Content-Length,没有缓存策略。浏览器每次刷新都可能发请求,服务端还得读文件、算 ETag、比对时间戳。
-
e.Static()不支持传入自定义 Header,无法注入Cache-Control - 你也不能在中间件里对静态文件响应统一加头——因为
e.Static()是内部处理并直接 writeHeader + writeBody,中间件早已执行完毕 - 试图用
c.Response().Header().Set("Cache-Control", "...")在静态路由 handler 里操作?没用:Echo 的Static不走你写的 handler
Nginx 动静分离时怎么加强缓存头
这是最常用也最推荐的方式:让 Nginx 拦截静态路径,自己读文件并添加缓存头,完全绕过 Echo。
- 必须用
location ^~ /static/(不是location /static/),确保优先级高于正则匹配规则 -
root /var/www/html;表示静态文件根目录是/var/www/html,所以请求/static/css/main.css对应磁盘路径/var/www/html/static/css/main.css - 用
expires 365d;或add_header Cache-Control "public, max-age=31536000, immutable";——注意immutable能防止浏览器在地址栏按回车时发起条件请求 - 别漏掉
add_header Last-Modified "";(可选),避免某些旧浏览器仍发If-Modified-Since
location ^~ /static/ {
root /var/www/html;
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
}
如果必须用 Echo 自己 serve 静态文件(比如开发环境或无 Nginx 场景)
得放弃 e.Static(),改用手动读取 + 设置 Header 的方式,用 e.GET() 拦截路径并控制响应:
- 路径需显式注册,比如
e.GET("/static/*", staticHandler),其中*是通配参数 - 在 handler 里用
c.Param("*")拼出真实文件路径,检查是否存在、是否在白名单目录内(防路径遍历) - 调用
http.ServeFile(c.Response(), c.Request(), filepath)前,手动 setCache-Control和Expires - 注意:这种方式无法利用操作系统 sendfile 优化,性能低于 Nginx,且要自己处理 MIME 类型和 404
关键代码片段:
e.GET("/static/*", func(c echo.Context) error {
path := c.Param("*")
fullPath := filepath.Join("public", path)
if !strings.HasPrefix(filepath.Clean(fullPath), "public") {
return echo.NewHTTPError(http.StatusForbidden)
}
c.Response().Header().Set("Cache-Control", "public, max-age=31536000, immutable")
c.Response().Header().Set("Expires", time.Now().Add(365*24*time.Hour).UTC().Format(http.TimeFormat))
http.ServeFile(c.Response(), c.Request(), fullPath)
return nil
})
真正起作用的从来不是框架的某个函数名,而是最终发出的响应头。Nginx 配置里少一个 expires,或者 Echo 手动 serve 时忘了 set Cache-Control,浏览器就永远不会跳过请求——这点容易被忽略,但恰恰决定了静态资源是否真“静态”。



















