rgoussu@goussu: ~/library/go/web-frameworks
~/library/go/web-frameworks cat fiber.md

Fiber deep dive

# Fiber's Express-style API on fasthttp — what abandoning net/http buys, the recycled-context hazard, and when the throughput trade is worth it.

Conceptsaved 2026-08-09 #go#web#frameworks#fiber#fasthttp#performance

Overview

Fiber is the deliberate outlier of the Go web world: an Express-inspired framework built on fasthttp instead of net/http. fasthttp rewrites the HTTP layer around aggressive object reuse — request and response objects are pooled and recycled — which wins striking micro-benchmark numbers at the price of breaking the http.Handler contract entirely. Choosing Fiber means opting out of the stdlib ecosystem: middleware, instrumentation, and idioms that assume net/http no longer apply, and a class of subtle use-after-recycle bugs becomes possible. It is a real tool for a narrow job, not a default.

Key points

  • fasthttp, not net/http: no http.Request/http.ResponseWriter anywhere; handlers are func(*fiber.Ctx) error over fasthttp's RequestCtx.
  • What fasthttp changes: pooled request/response objects, []byte-first APIs, its own connection handling — the "zero allocations in the hot path" claims come from recycling, not magic.
  • The dangerous part — context recycling: everything obtained from *fiber.Ctx (c.Params, c.Body, c.Query, header values) points into buffers that are reused after the handler returns. Keeping such a reference — in a goroutine, a closure, a cache, a channel send — is a data race producing silently corrupted values. Copy first (utils.CopyString, explicit append([]byte(nil), b...)) or run with the Immutable config, which copies everything and forfeits much of the speed.
  • Ecosystem cut-off: func(http.Handler) http.Handler middleware — otelhttp, promhttp handlers, most of what the Go observability world ships — cannot be used natively. The adaptor package shims both directions, but each crossing pays conversion overhead that erodes the point of fasthttp.
  • HTTP/2 story is weak: fasthttp targets HTTP/1.1; there is no first-class HTTP/2 (or h2c/gRPC) support comparable to net/http's. Services behind an HTTP/2-terminating proxy dodge this; anything needing end-to-end HTTP/2 should not pick Fiber.
  • When the trade makes sense: extreme-throughput, small-payload microservices — proxies, token endpoints, real-time bidding — where the service is CPU-bound on HTTP parsing and allocation, measured, and the team accepts the discipline.
  • When it doesn't: typical CRUD-over-database services, where I/O dominates and the benchmark gap vanishes while the interop and safety costs remain.
  • Express familiarity is the other honest draw: app.Get("/users/:id", h), mounted routers, and a first-party middleware set (logger, recover, cors, limiter, cache, compress, jwt via contrib) mirror the Node mental model closely.

Details

The recycling hazard, concretely

app.Get("/items/:id", func(c *fiber.Ctx) error {
    id := c.Params("id")        // string header into a reused buffer
    go audit(id)                // BUG: by the time audit runs, the buffer
                                // may hold a different request's bytes
    return c.SendString("ok")
})

The fix is a copy at the boundary: id := utils.CopyString(c.Params("id")). The bug class is nasty precisely because it passes low-traffic tests and appears under production concurrency as wrong data, not a crash. Teams adopting Fiber need a code-review rule: nothing derived from *fiber.Ctx outlives the handler uncopied.

Interop via adaptor

adaptor.HTTPHandler(h http.Handler) fiber.Handler and its inverse adaptor.FiberHandler bridge the two worlds by materializing a net/http request from the fasthttp one (and back). It works for occasional endpoints — mounting a promhttp metrics handler — but wrapping every route in adapters recreates net/http allocation per request, at which point chi + stdlib is the simpler, safer equivalent.

Configuration posture

fiber.New(fiber.Config{...}) exposes Prefork (SO_REUSEPORT multi-process listening), Immutable, body limits, and timeout knobs. Prefork helps specific saturated-listener workloads but breaks in-process shared state and complicates observability — measure before enabling, same rule as choosing Fiber itself.

Related