必须禁用Recovery中间件向HTTP响应体写入panic堆栈,仅记录脱敏日志;Logger中间件需提前禁用body消费、计算并存储body哈希,再还原body供后续使用;RequestID和UA须通过ctx.Locals统一传递,且Logger必须在Recovery前注册。

Recovery中间件默认会把panic堆栈写进HTTP响应体
很多开发者开了 fiber.Recover 就以为“有错误堆栈了”,结果上线后发现客户端能直接看到完整 panic 信息——比如数据库连接失败时暴露了密码、路径或内部结构。这是因为 Recovery 默认行为是调用 w.Write() 把堆栈原样塞进响应体,而没做任何脱敏或拦截。
必须禁用响应写入,只记日志不返给客户端
正确做法是在 Recovery 配置里重写 StackTraceHandler,只调用日志器,绝不碰 ctx.Send() 或 w.Write()。同时要在 handler 结束后手动清空已写入的响应缓冲区,否则可能残留部分 panic 内容。
- 配置 Recovery 时传入自定义
StackTraceHandler函数 - 函数体内只调用
log.Error(...)或你用的结构化日志器(如zerolog) - handler 返回前执行
ctx.Response().Reset(),确保响应体干净 - 绝对不要在
StackTraceHandler里调用ctx.Status(500).SendString(...)
Logger中间件要提前捕获请求体哈希,否则审计无依据
单纯记录 panic 堆栈没用,如果不知道这个 panic 是哪个请求触发的,根本没法复现。而 fiber.Ctx.Body() 只能读一次——Logger 中间件一读,后续 Recovery 或业务 handler 就读不到 body 了。
- 在 Logger 中间件开头调用
ctx.Request().DisableBodyConsumption() - 用
io.ReadAll(ctx.Request().Body())拿到原始 body 字节 - 计算 SHA256 哈希并存入上下文(
ctx.Locals("body_hash", hash)) - 再把 body 包装回
ctx.Request().SetBodyRaw(buf.Bytes()),供后续 handler 使用
panic发生时,RequestID和User-Agent必须能对上日志
如果 Recovery 日志里没有 RequestID,或者 Logger 和 Recovery 记录的 UA 不一致,说明中间件执行顺序错了,或者上下文被覆盖。Fiber 的中间件是链式执行,Logger 必须在 Recovery 之前注册,且两者都要从同一个 ctx 提取字段。
- 用
fiber.New(fiber.Config{EnablePrint: false})关掉默认控制台输出,避免干扰 - Logger 中间件中生成唯一
X-Request-ID并写入响应头,同时存入ctx.Locals - Recovery 的
StackTraceHandler从ctx.Locals读取该 ID 和 UA,拼进日志消息 - 别依赖
r.Header.Get("X-Request-ID")—— header 可能被篡改,Locals才可信


















