Iris 的 Cache-Control 不能靠中间件全局禁用,因为静态服务逻辑在中间件执行前已写死响应头;必须在 handler 内显式调用 ctx.Header() 设置,且需在任何 Write 操作之前。

为什么 Iris 的 Cache-Control 不能靠中间件全局禁用
Iris 默认不自动加缓存头,但一旦你用了 ctx.ServeFile()、ctx.ServeContent() 或静态文件服务(如 app.HandleDir()),它会按 MIME 类型自动设置 Cache-Control: public, max-age=3600 等值。这不是 bug,是设计行为——但很多 API 接口(比如返回实时订单状态的 /api/v1/order/status)绝不能被 CDN 或浏览器缓存。你不能指望在某个中间件里统一 ctx.Header("Cache-Control", "no-store") 就完事,因为静态服务逻辑在路由匹配后、中间件执行前就已写死响应头。
在具体 Handler 中覆盖 Cache-Control 头最可靠
对需要禁用缓存的接口,必须在对应 handler 函数内部显式设置响应头,且要放在任何 ctx.Write* 调用之前。Iris 不提供“路由级缓存开关”配置项,一切以 handler 内部控制为准。
-
ctx.Header("Cache-Control", "no-cache, no-store, must-revalidate")是最严格组合,强制跳过所有缓存层 - 若只需禁用浏览器缓存但允许 CDN 缓存(如某些带签名的临时资源),用
"no-cache, max-age=0" - 注意:不要只设
"no-cache",它仍允许缓存,只是每次使用前要校验 - 如果你用
ctx.JSON()或ctx.StatusCode(),它们不会覆盖你提前写的Header,顺序安全
app.Get("/api/v1/user/current", func(ctx iris.Context) {
ctx.Header("Cache-Control", "no-store")
ctx.JSON(map[string]interface{}{"id": 123, "name": "alice"})
})
静态资源路径下如何禁用缓存
当你用 app.HandleDir("/static", "./public") 提供前端资源时,Iris 会对所有匹配文件自动加缓存头。想让其中某类文件(比如 /static/config.json)不缓存,不能改路由,只能换方式提供:
- 把该文件注册为独立路由,走 handler 控制:
app.Get("/static/config.json", configHandler) - 避免用
HandleDir暴露敏感配置,改用ctx.ServeFile()+ 手动设头 - 如果必须用
HandleDir,又想整体降低缓存强度,可传入自定义iris.DirOptions,但仅支持统一设置CacheControl字符串,无法按路径区分
也就是说,Iris 没有类似 Express 的 res.set("Cache-Control", ...).sendFile() 链式调用,也没有 per-path cache 策略 DSL。路径粒度的缓存控制,只能靠拆路由或重写 handler 实现。
容易忽略的陷阱:gzip 压缩与缓存头冲突
如果你启用了 iris.Gzip 中间件,而同时手动设置了 Cache-Control: no-store,部分旧版代理(如某些企业防火墙)可能因压缩后响应体变化,错误地缓存了未压缩版本。这不是 Iris 的问题,但实际联调中常被归咎于框架。
- 验证方法:用
curl -I检查响应头是否真的生效,而非只看浏览器 DevTools 的 “Size” 列 - 若发现
Vary: Accept-Encoding缺失,说明 gzip 中间件未正确介入,需确认它在路由链中的位置是否早于你的 handler - 生产环境建议配合
ETag或Last-Modified使用no-cache,比单纯no-store更利于调试
真正难处理的不是怎么写那行 ctx.Header,而是得时刻意识到:Iris 的缓存行为是“按服务类型隐式触发”的,而不是“按路由显式声明”的。你得主动去破那个默认假设,而不是等它出问题再补。


















