Fiber中间件中c.Set()可直接设置响应头,无需额外包装;但需注意未调用c.Send()等终结方法前才有效,且易被后续handler覆盖或flush后失效。

中间件里直接写 c.Set() 就够了
不需要额外包装、不依赖第三方库,Fiber 的中间件本身就是 *fiber.Ctx 的处理链,c.Set() 会直接写入响应头缓冲区。只要还没调用 c.Send()、c.JSON() 等终结方法,后续 handler 都能叠加或覆盖。
常见错误现象:在中间件里调了 c.Set("X-Trace-ID", "abc"),但浏览器看不到这个 header——大概率是业务 handler 又调了一次 c.Set("X-Trace-ID", "def") 覆盖了,或者用了 c.Status(200).SendString("ok") 这类组合方法,它内部会立即 flush 响应头,之后再 Set 就无效了。
- 放行逻辑里才调
c.Next();拒绝请求直接return,别让它继续往下走 - 如果要强制所有响应都带某个 header(比如
Cache-Control),就用app.Use()全局挂载,别只挂到某个app.Get() -
c.Set()是大小写不敏感的,c.Set("content-type", "application/json")和c.Set("Content-Type", ...)效果一样
跨域场景下必须配全 Access-Control- 头
只加 Access-Control-Allow-Origin: * 不够,浏览器预检(OPTIONS)失败时根本不会发主请求。Fiber 没有内置 CORS 中间件(v3 已移除),得手动补全关键头:
-
c.Set("Access-Control-Allow-Origin", "https://example.com")—— 不能和Credentials同时用* -
c.Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,PATCH")—— 列出你真支持的动词 -
c.Set("Access-Control-Allow-Headers", "Content-Type,X-Api-Key")—— 包含客户端实际带的自定义 header -
c.Set("Access-Control-Allow-Credentials", "true")—— 如果前端设了credentials: 'include'
注意:OPTIONS 请求必须被显式处理,否则 app.Use() 里设的 header 对预检无效。建议单独注册:app.Options("*", func(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) }),再配合上面的 Set。
app.Use() 和 app.Get() 挂中间件的区别
前者对所有 HTTP 方法 + 所有匹配前缀路径生效;后者只对 GET 方法生效,且路径必须精确匹配(比如 app.Get("/api", ...) 不会匹配 /api/users)。想给整个 API 前缀统一加 header,必须用 app.Use("/api")。
-
app.Use("/api")匹配/api、/api/、/api/users、/api/v1/health -
app.Use("/api/")不匹配/api(结尾无斜杠),但匹配/api/users - 别依赖
StrictRouting: false来“兜底”,它会让路由匹配变模糊,反而导致 header 漏加
响应头被覆盖或丢失的隐藏原因
最常被忽略的是 fasthttp 底层复用机制:c.Set() 写入的是当前请求上下文的 header map,但如果 handler panic 或提前返回,某些中间件(比如 Recover)可能重置响应状态,把之前设的 header 清空。另外,Timeout 中间件触发后,原 handler 若没检查 c.IsAborted() 就继续执行,也可能在已 flush 的响应上再 Set,结果被忽略。
- 上线前务必关掉
fiber.Default(),它自带的Logger中间件会在每次响应后强行覆盖Content-Length,干扰你设的其他 header - 调试时用
curl -v http://localhost:3000/path直接看原始响应头,比浏览器开发者工具更可靠 - 如果 header 值含特殊字符(如空格、中文),要用
c.Append()替代c.Set(),避免被 fasthttp 自动编码


















