Jesse A. Green / notebook

Fiber User Guide

Generated June 2026 from gofiber/fiber at dec796e

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Fiber is an Express-inspired web framework for Go, built on top of fasthttp — one of the fastest HTTP engines available for Go. If you have ever built a web app or API in Node.js with Express, Fiber will feel immediately familiar: you create an app, attach routes and middleware, read the request and write the response through a single context object, and start listening.

This guide covers Fiber v3 (module path github.com/gofiber/fiber/v3). It walks you through installing Fiber, defining routes, working with the request/response context, parsing incoming data, applying the built-in middleware, and shipping a production server. Every command, method, option, and default in this guide comes directly from the Fiber source code.

Note

Fiber v3 is a major version with a different import path and API from v2. Code written for github.com/gofiber/fiber/v2 will not compile against v3 unchanged. The most visible change is the handler signature — v3 uses func(c fiber.Ctx) error (a value, fiber.Ctx, not a pointer *fiber.Ctx). See Migrating from v2.


1. Quick Start

Here is a complete Fiber program. It creates an app, defines one route, and serves it on port 3000.

package main

import (
    "log"

    "github.com/gofiber/fiber/v3"
)

func main() {
    // Initialize a new Fiber app
    app := fiber.New()

    // Define a route for GET on the root path "/"
    app.Get("/", func(c fiber.Ctx) error {
        return c.SendString("Hello, World 👋!")
    })

    // Start the server on port 3000
    log.Fatal(app.Listen(":3000"))
}

Save it as main.go, then run:

go run main.go

Expected output:


 ┌───────────────────────────────────────────────────┐
 │                   Fiber v3.x.x                     │
 │               http://127.0.0.1:3000               │
 │                                                   │
 │ Handlers ............. 2  Processes ........... 1 │
 │ Prefork ....... Disabled  PID ............. 12345 │
 └───────────────────────────────────────────────────┘

Visit http://localhost:3000 in your browser, or:

curl http://localhost:3000
Hello, World 👋!

Tip

Every Fiber handler has the signature func(c fiber.Ctx) error. You read the request and write the response through c, and you return an error instead of writing to a separate http.ResponseWriter. Returning a non-nil error hands control to Fiber's error handler.


2. Installation

Fiber requires Go version 1.25 or higher. Check your version with go version; if you need to install or upgrade, visit the official Go download page.

Important

Go 1.25+ is a hard requirement for Fiber v3. Fiber uses the unsafe package for zero-allocation optimizations and is tested against Go 1.25 and newer. Older Go toolchains will fail to build.

New project

# Create and enter a project directory
mkdir myapp && cd myapp

# Initialize a Go module
go mod init github.com/your/repo

# Add Fiber
go get -u github.com/gofiber/fiber/v3

Existing module

go get -u github.com/gofiber/fiber/v3

The go get command fetches Fiber and records it in your go.mod and go.sum. Import it as github.com/gofiber/fiber/v3 and you are ready to build.

Note

The /v3 suffix is part of the import path, not a folder you create. This is standard Go module versioning. In your code you still refer to the package as fiber (fiber.New(), fiber.Ctx, …).


3. Routing

Routing maps an HTTP method and URL path to a handler. Fiber gives you one method per HTTP verb on the app (and on groups and sub-routers).

Basic routes

app.Get("/",          handler)  // GET
app.Post("/users",    handler)  // POST
app.Put("/users/:id", handler)  // PUT
app.Patch("/users/:id", handler) // PATCH
app.Delete("/users/:id", handler) // DELETE
app.Head("/health",   handler)  // HEAD
app.Options("/users", handler)  // OPTIONS

You can also register a handler for all methods, or for a custom set:

app.All("/any", handler)                 // every HTTP method
app.Add([]string{"GET", "POST"}, "/x", handler) // a specific set

Note

By default Fiber automatically registers a HEAD route for every GET route, so curl -I works without extra code. You can disable this with the DisableHeadAutoRegister config option if you want to supply custom HEAD handlers.

Route parameters

Capture parts of the path with :name. Read them with c.Params("name").

// GET /user/fenny
app.Get("/user/:name", func(c fiber.Ctx) error {
    return c.SendString("Hello, " + c.Params("name")) // "Hello, fenny"
})

Fiber supports several parameter styles:

Pattern Matches Example route Access
:name A single required segment /user/:name c.Params("name")
:name? An optional segment /user/:name? c.Params("name") (may be "")
* A wildcard (greedy) /api/* c.Params("*") or c.Params("*1")
+ A wildcard (one or more) /files/+ c.Params("+")
:from-:to Multiple params in a segment /flights/:from-:to c.Params("from"), c.Params("to")
:file.:ext Params split on . /:file.:ext c.Params("file"), c.Params("ext")
// GET /flights/LAX-SFO
app.Get("/flights/:from-:to", func(c fiber.Ctx) error {
    return c.SendString(c.Params("from") + " → " + c.Params("to")) // "LAX → SFO"
})

// GET /api/anything/here  →  c.Params("*") == "anything/here"
app.Get("/api/*", func(c fiber.Ctx) error {
    return c.SendString(c.Params("*"))
})

For multiple wildcards, address them by index: c.Params("*1"), c.Params("*2").

Tip

Use the generic helper fiber.Params[T](c, key) to parse a parameter directly into a typed value: fiber.Params[int](c, "id") returns an int (and 0 if the segment is missing or unparseable). Supported types: all int/uint sizes, float32/64, bool, string, and []byte.

Route constraints

Constrain a parameter so the route only matches when the value has the right shape. Append the constraint in angle brackets after the parameter name.

// Only matches when :id is an integer
app.Get("/user/:id<int>", handler)

// Only matches a minimum length of 5
app.Get("/category/:name<minLen(5)>", handler)

Built-in constraints include int, bool, float, alpha, guid, minLen(n), maxLen(n), len(n), min(n), max(n), range(min,max), datetime(format), and regex(...). If a request does not satisfy the constraint, Fiber treats the route as not matched and continues searching.

Naming routes and generating URLs

Give a route a name with .Name(), then build a URL to it later with c.GetRouteURL():

app.Get("/user/:id", handler).Name("user.show")

app.Get("/link", func(c fiber.Ctx) error {
    url, _ := c.GetRouteURL("user.show", fiber.Map{"id": 42})
    return c.SendString(url) // "/user/42"
})

fiber.Map is a convenience alias for map[string]any, used throughout Fiber for JSON bodies, view data, and route parameters.


4. The Context (fiber.Ctx)

The context, fiber.Ctx, is the single object you use inside every handler. It holds the incoming request and the outgoing response, and exposes methods for reading request data and writing the response.

Caution

Fiber reuses the context across requests for performance ("zero allocation"). Values returned by Ctx methods — c.Params(), c.Query(), c.Body(), c.Get(), and so on — are only valid inside the handler. Do not store these values or pass c itself to a goroutine that outlives the handler. If you need a value after the handler returns, copy it (e.g. id := strings.Clone(c.Params("id"))), or enable the Immutable config option. To carry a cancellation/deadline into a goroutine, use c.Context() — never c directly. See How It Works.

Reading the request

Method Returns
c.Method() HTTP method, e.g. "GET"
c.Path() URL path, e.g. "/users"
c.OriginalURL() Full original URL with query string
c.BaseURL() Protocol + host, e.g. "https://example.com"
c.Params("id") A route parameter
c.Query("q") A query-string value (?q=...)
c.Queries() All query parameters as map[string]string
c.Get("X-Header") A request header (case-insensitive)
c.Cookies("name") A request cookie value
c.Body() / c.BodyRaw() The request body bytes (Body() auto-decompresses)
c.FormValue("name") A form field value
c.FormFile("file") An uploaded file (*multipart.FileHeader)
c.IP() / c.IPs() Client IP / the X-Forwarded-For chain
c.Hostname() / c.Host() Hostname / host (with port)
c.Secure() true over TLS
c.Accepts("json") Content negotiation against the Accept header
app.Get("/search", func(c fiber.Ctx) error {
    term := c.Query("q")              // ?q=fiber  → "fiber"
    page := fiber.Query[int](c, "page", 1) // typed, with default 1
    return c.SendString(term + " page " + strconv.Itoa(page))
})

Tip

Like Params, both Query and Locals have generic forms: fiber.Query[int](c, "page"), fiber.Locals[string](c, "user"). They parse and type-assert in one call and fall back to the zero value (or your supplied default) when the key is missing.

Writing the response

Method Effect
c.SendString("hi") Write a plain string body
c.Send([]byte{...}) Write raw bytes
c.JSON(data) Serialize to JSON, set Content-Type: application/json
c.XML(data) Serialize to XML
c.CBOR(data) / c.MsgPack(data) Serialize to CBOR / MessagePack (binary)
c.SendStatus(code) Set status code (and matching status text if body is empty)
c.Status(code) Set status code (chainable — returns c)
c.Set("X-Header", "v") Set a response header
c.Type("json") Set Content-Type from a file extension (chainable)
c.SendFile("./path") Send a file with the right content type
c.Download("./report.pdf") Send a file as a download attachment
c.Cookie(&fiber.Cookie{...}) Set a response cookie
c.Redirect().To("/other") Issue an HTTP redirect
c.Render("view", data) Render an HTML template

Status is chainable, so the idiomatic one-liner is:

app.Get("/teapot", func(c fiber.Ctx) error {
    return c.Status(fiber.StatusTeapot).SendString("🍵 short and stout")
})

Returning JSON

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

app.Get("/user", func(c fiber.Ctx) error {
    return c.JSON(User{Name: "John", Age: 20})
    // => {"name":"John","age":20}
})

app.Get("/map", func(c fiber.Ctx) error {
    return c.JSON(fiber.Map{"success": true, "message": "Hi John!"})
})

You can pass a custom content type as a second argument, useful for RFC 7807 problem documents:

return c.JSON(fiber.Map{
    "type":   "https://example.com/probs/out-of-credit",
    "title":  "You do not have enough credit.",
    "status": 403,
}, "application/problem+json")

Content negotiation

c.AutoFormat(data) picks a representation based on the request's Accept header — text/plain, text/html, application/json, application/xml, MsgPack, or CBOR — automatically:

app.Get("/auto", func(c fiber.Ctx) error {
    return c.AutoFormat(User{Name: "John"})
    // Accept: application/json → {"name":"John"}
    // Accept: text/html        → <p>{John}</p>
})

For full control over which formats you offer, use c.Format(...) with explicit fiber.ResFmt handlers.

Setting cookies

app.Get("/login", func(c fiber.Ctx) error {
    c.Cookie(&fiber.Cookie{
        Name:     "session",
        Value:    "randomvalue",
        Expires:  time.Now().Add(24 * time.Hour),
        HTTPOnly: true,
        Secure:   true,
        SameSite: "Lax",
    })
    return c.SendStatus(fiber.StatusOK)
})

Important

When you set SameSite: "None", Fiber automatically forces Secure: true, as required by modern browsers (RFC 6265bis). A SameSite=None cookie without Secure is rejected by browsers, so Fiber corrects this for you.

To delete a cookie, set it again with the same attributes and Expires: fasthttp.CookieExpireDelete, or call c.ClearCookie("name"). Note that browsers only clear a cookie when the path/domain attributes match the original, so for path- or domain-scoped cookies you must re-issue with the same Path/Domain.


5. Reading & Binding Request Data

For anything beyond a single value, use Fiber's binder to decode the request directly into a Go struct. Access it with c.Bind().

Binding the body

c.Bind().Body(&v) inspects the request's Content-Type and decodes accordingly. Tag your struct fields for each format you want to accept:

type Person struct {
    Name string `json:"name" xml:"name" form:"name" msgpack:"name"`
    Pass string `json:"pass" xml:"pass" form:"pass" msgpack:"pass"`
}

app.Post("/", func(c fiber.Ctx) error {
    p := new(Person)
    if err := c.Bind().Body(p); err != nil {
        return err
    }
    return c.JSON(p)
})

The content type determines which tag Fiber reads:

Content-Type Struct tag
application/json json
application/xml, text/xml xml
application/x-www-form-urlencoded form
multipart/form-data form
application/vnd.msgpack msgpack
curl -X POST -H "Content-Type: application/json" \
  --data '{"name":"john","pass":"doe"}' localhost:3000

Binding from other sources

Each request source has its own binder method and struct tag:

Method Source Tag
c.Bind().URI(&v) Path parameters uri
c.Bind().Query(&v) Query string query
c.Bind().Header(&v) Request headers header
c.Bind().Cookie(&v) Cookies cookie
c.Bind().RespHeader(&v) Response headers respHeader
c.Bind().All(&v) All of the above (mixed tags)

All merges every source into one struct, with this precedence (later sources do not override earlier ones): URI → Body → Query → Headers → Cookies.

type Req struct {
    ID    int    `uri:"id"`
    Name  string `query:"name" json:"name"`
    Role  string `header:"X-User-Role"`
}

app.Post("/users/:id", func(c fiber.Ctx) error {
    r := new(Req)
    if err := c.Bind().All(r); err != nil {
        return err
    }
    return c.JSON(r)
})

Array query parameters

Fiber understands several array encodings in the query string:

Format Example Needs EnableSplittingOnParsers?
Repeated key ?colors=red&colors=blue No
Bracket notation ?colors[]=red&colors[]=blue No
Comma-separated ?colors=red,blue Yes
Indexed bracket (nested) ?posts[0][title]=Hello No

Note

Comma-splitting is off by default. Without EnableSplittingOnParsers: true in your config, ?colors=red,blue binds as a single element ["red,blue"], not two. Prefer repeated keys (?colors=red&colors=blue) if you do not want to change the global config.

Default values

Set field defaults inline with the default: tag option:

type Filter struct {
    Name     string   `query:"name,default:john"`
    Products []string `query:"products,default:shoe|hat"` // slice items joined by |
}

Validation

Fiber does not ship a validator, but it integrates with any validator that implements the StructValidator interface. The popular choice is go-playground/validator:

import "github.com/go-playground/validator/v10"

type structValidator struct{ validate *validator.Validate }

func (v *structValidator) Validate(out any) error {
    return v.validate.Struct(out)
}

app := fiber.New(fiber.Config{
    StructValidator: &structValidator{validate: validator.New()},
})

type Person struct {
    Name string `json:"name" validate:"required"`
    Age  int    `json:"age"  validate:"gte=18,lte=60"`
}

app.Post("/", func(c fiber.Ctx) error {
    p := new(Person)
    if err := c.Bind().JSON(p); err != nil { // runs validation too
        return err // validation failures surface here
    }
    return c.JSON(p)
})

When a validator is configured, binding runs it automatically for struct destinations. Call c.Bind().SkipValidation(true) to bypass it for a single bind.

Tip

By default the binder returns parse errors to you (*fiber.BindError, which carries the Source — "uri", "query", "body", … — so you can return RFC-correct status codes). Call c.Bind().WithAutoHandling() to instead have Fiber convert any parse failure into a 400 Bad Request automatically.


6. Middleware

Middleware are handlers that run before (and optionally after) your route handler. Register them with app.Use(...). A middleware typically does some work, then calls c.Next() to pass control down the chain.

app.Use(func(c fiber.Ctx) error {
    fmt.Println("Request:", c.Method(), c.Path())
    return c.Next() // continue to the next handler
})

Scope middleware to a path prefix by passing it first:

app.Use("/api", authMiddleware) // only runs for paths under /api

Built-in middleware

Fiber ships a comprehensive set of middleware in github.com/gofiber/fiber/v3/middleware/*. Import the one you need and mount its New() constructor. Each accepts an optional config struct.

Middleware Import path suffix What it does
logger middleware/logger Logs each request/response
recover middleware/recover Recovers from panics and routes them to the error handler
cors middleware/cors Cross-Origin Resource Sharing headers
csrf middleware/csrf CSRF token protection for unsafe requests
basicauth middleware/basicauth HTTP Basic authentication
keyauth middleware/keyauth API-key authentication
limiter middleware/limiter Rate limiting
session middleware/session Server-side sessions
compress middleware/compress gzip / brotli / zstd / deflate response compression
cache middleware/cache Caches responses
etag middleware/etag Adds ETag headers for conditional requests
helmet middleware/helmet Sets common security headers
encryptcookie middleware/encryptcookie Encrypts cookie values
idempotency middleware/idempotency Makes unsafe requests idempotent via a key
requestid middleware/requestid Attaches a unique ID to each request
healthcheck middleware/healthcheck Liveness / readiness probe endpoints
static middleware/static Serves static files and directories
proxy middleware/proxy Reverse-proxies requests to upstreams
redirect middleware/redirect Path-based redirects
rewrite middleware/rewrite Rewrites the URL path by rules
timeout middleware/timeout Enforces a per-request time limit
earlydata middleware/earlydata Supports TLS 1.3 0-RTT early data
favicon middleware/favicon Serves / ignores /favicon.ico
envvar middleware/envvar Exposes selected environment variables as JSON
expvar middleware/expvar Exposes runtime metrics in expvar format
pprof middleware/pprof Serves Go pprof profiling endpoints
paginate middleware/paginate Parses page/offset/cursor pagination from the query string
responsetime middleware/responsetime Writes request duration to a response header
hostauthorization middleware/hostauthorization Allowlists the Host header (anti DNS-rebinding)
adaptor middleware/adaptor Converts handlers to/from net/http
skip middleware/skip Skips a wrapped handler when a predicate is true
sse middleware/sse Server-Sent Events helpers

Common middleware examples

Logging and panic recovery — the two you will almost always want:

import (
    "github.com/gofiber/fiber/v3/middleware/logger"
    "github.com/gofiber/fiber/v3/middleware/recover"
)

app.Use(recover.New()) // catch panics
app.Use(logger.New())  // log requests

CORS — allow browser requests from other origins:

import "github.com/gofiber/fiber/v3/middleware/cors"

app.Use(cors.New(cors.Config{
    AllowOrigins: []string{"https://example.com"},
    AllowMethods: []string{"GET", "POST"},
}))

Warning

With default config, cors.New() sets AllowOrigins to ["*"] — it allows every origin. That is convenient for local development but is rarely what you want in production. Set an explicit AllowOrigins list. Also note: you cannot combine the "*" wildcard with AllowCredentials: true; Fiber rejects that combination because it is a security risk.

Rate limiting — cap requests per client:

import "github.com/gofiber/fiber/v3/middleware/limiter"

app.Use(limiter.New(limiter.Config{
    Max:        20,               // 20 requests …
    Expiration: 30 * time.Second, // … per 30 seconds, per IP
}))

Defaults are Max: 5 over Expiration: 1 minute, keyed by c.IP(). When the limit is hit, the client gets 429 Too Many Requests plus X-RateLimit-* and Retry-After headers.

Basic auth:

import "github.com/gofiber/fiber/v3/middleware/basicauth"

app.Use(basicauth.New(basicauth.Config{
    Users: map[string]string{"admin": "secret"},
}))

Sessions:

import "github.com/gofiber/fiber/v3/middleware/session"

store := session.NewStore()

app.Get("/", func(c fiber.Ctx) error {
    sess, err := store.Get(c)
    if err != nil {
        return err
    }
    count, _ := sess.Get("count").(int)
    count++
    sess.Set("count", count)
    if err := sess.Save(); err != nil {
        return err
    }
    return c.SendString(fmt.Sprintf("visit #%d", count))
})

Caution

The default session and CSRF cookies are not marked Secure or HTTPOnly. For any production deployment over HTTPS, set CookieSecure: true and CookieHTTPOnly: true in the respective middleware config. See the Admin Guide security section for the hardened configuration.


7. Static Files

Serve a directory of assets with the static middleware. Register it on a wildcard route:

import "github.com/gofiber/fiber/v3/middleware/static"

// Serve ./public at the site root
app.Get("/*", static.New("./public"))
// => /js/script.js, /css/style.css

// Serve under a prefix
app.Get("/assets*", static.New("./public"))

// Serve a single file (e.g. an SPA fallback) for any unmatched path
app.Get("*", static.New("./public/index.html"))

For one-off files inside a handler, use c.SendFile():

app.Get("/logo", func(c fiber.Ctx) error {
    return c.SendFile("./public/logo.png")
})

SendFile accepts an optional fiber.SendFile config for byte-range requests, compression, download mode, and cache duration:

return c.SendFile("style.css", fiber.SendFile{
    Compress:  true,
    ByteRange: true,
})

8. Grouping Routes & Sub-apps

Groups

Group routes that share a prefix and/or middleware with app.Group():

api := app.Group("/api", authMiddleware) // /api, with auth

v1 := api.Group("/v1")                   // /api/v1
v1.Get("/users", listUsers)              // GET /api/v1/users

v2 := api.Group("/v2")                   // /api/v2
v2.Get("/users", listUsersV2)

app.Route() does the same with a callback, and supports a name prefix:

app.Route("/test", func(r fiber.Router) {
    r.Get("/foo", handler).Name("foo") // /test/foo (name: test.foo)
    r.Get("/bar", handler).Name("bar") // /test/bar (name: test.bar)
}, "test.")

Chained routes

app.RouteChain() declares several verbs on one path, fluently:

app.RouteChain("/events").
    All(func(c fiber.Ctx) error { return c.Next() }). // route-specific middleware
    Get(listEvents).
    Post(createEvent)

Mounting a sub-app

You can mount one Fiber app inside another with app.Use():

app := fiber.New()
micro := fiber.New()

app.Use("/admin", micro) // requests to /admin/* go to micro

micro.Get("/dashboard", func(c fiber.Ctx) error {
    return c.SendString("Admin Dashboard")
})

Note

Unlike Express, Fiber does not strip the mount prefix. Inside micro, c.Path() still returns the full request path (/admin/dashboard, not /dashboard). There is no req.baseUrl equivalent. Register a sub-app's routes before mounting it.

Domain routing

Scope routes to a hostname with app.Domain():

app.Domain("api.example.com").Get("/users", handler)

// With a parameter
app.Domain(":tenant.example.com").Get("/", func(c fiber.Ctx) error {
    return c.SendString(fiber.DomainParam(c, "tenant") + "'s site")
})

9. Templates & Views

Fiber renders HTML through a view engine. With no engine configured it uses Go's standard html/template. To use another engine (Pug, Handlebars, Mustache, Amber, …), install one from the gofiber/template collection and set it in the config:

import "github.com/gofiber/template/html/v2"

engine := html.New("./views", ".html")

app := fiber.New(fiber.Config{Views: engine})

app.Get("/", func(c fiber.Ctx) error {
    return c.Render("index", fiber.Map{
        "Title": "Homepage",
        "Year":  2026,
    })
})

c.Render(name, data, layouts...) produces a text/html response. Use c.ViewBind() in middleware to add variables shared across all renders, and set ViewsLayout in the config for a global layout.

Tip

During development, call app.ReloadViews() (e.g. from a debug-only route or file watcher) to pick up template changes without restarting the server.


10. Error Handling

Fiber centralizes error handling. Your handlers and middleware simply return an error; Fiber routes it to a single error handler that writes the response.

app.Get("/", func(c fiber.Ctx) error {
    return c.SendFile("file-does-not-exist") // the error flows to the error handler
})

Errors with status codes

Use fiber.NewError(code, message) to attach an HTTP status to an error. Fiber also predefines an error for every standard status (fiber.ErrNotFound, fiber.ErrServiceUnavailable, …):

app.Get("/missing", func(c fiber.Ctx) error {
    return fiber.NewError(fiber.StatusNotFound, "user not found")
})

app.Get("/down", func(c fiber.Ctx) error {
    return fiber.ErrServiceUnavailable // 503 with standard text
})

The default error handler

By default, a generic error becomes 500 Internal Server Error; a *fiber.Error uses its embedded code and message, written as text/plain.

A custom error handler

Set ErrorHandler in the config to return tailored responses — for example, JSON errors for an API:

app := fiber.New(fiber.Config{
    ErrorHandler: func(c fiber.Ctx, err error) error {
        code := fiber.StatusInternalServerError
        var e *fiber.Error
        if errors.As(err, &e) {
            code = e.Code
        }
        return c.Status(code).JSON(fiber.Map{"error": err.Error()})
    },
})

Important

Fiber does not recover from panics by default — a panic in a handler will crash the worker. Add app.Use(recover.New()) so panics are caught and forwarded to your error handler instead.


11. The HTTP Client

Fiber includes a fasthttp-backed HTTP client in github.com/gofiber/fiber/v3/client for making outbound requests:

import "github.com/gofiber/fiber/v3/client"

cc := client.New()

resp, err := cc.Get("https://httpbin.org/get")
if err != nil {
    log.Fatal(err)
}
fmt.Println(resp.StatusCode())
fmt.Println(string(resp.Body()))

The client supports Get/Post/Put/Delete, JSON/XML/form bodies, query parameters, headers, cookies, a cookie jar, retries, and request/response hooks. It shares fasthttp's pooled, zero-allocation design with the server.


12. Going to Production

Graceful shutdown

Stop accepting new connections and let in-flight requests finish:

// In a goroutine, wait for a signal, then:
if err := app.ShutdownWithTimeout(10 * time.Second); err != nil {
    log.Printf("shutdown error: %v", err)
}

app.Shutdown() waits indefinitely; ShutdownWithTimeout force-closes after the deadline; ShutdownWithContext ties shutdown to a context. You can also pass a GracefulContext in the listen config to trigger shutdown automatically.

TLS / HTTPS

Serve HTTPS by giving Listen a certificate and key:

app.Listen(":443", fiber.ListenConfig{
    CertFile:    "./cert.pem",
    CertKeyFile: "./cert.key",
})

For automatic Let's Encrypt certificates, set an AutoCertManager; for full control, supply a *tls.Config via TLSConfig. See the Admin Guide for mTLS and ACME details.

Prefork

EnablePrefork spawns one Go process per CPU core, all listening on the same port, for higher throughput on multi-core machines:

app.Listen(":8080", fiber.ListenConfig{EnablePrefork: true})

Warning

Prefork processes do not share in-process memory. Any state you keep in a package variable (counters, in-memory caches, the default limiter/session memory store) is per-process and will not be consistent across workers. Use app.SharedState() or an external store (Redis, etc.) for shared state under prefork. Use fiber.IsChild() to tell parent and child processes apart.


13. Streaming & Real-Time

Server-Sent Events — stream events to the browser:

app.Get("/sse", func(c fiber.Ctx) error {
    c.Set("Content-Type", "text/event-stream")
    c.Set("Cache-Control", "no-cache")
    c.Set("Connection", "keep-alive")

    return c.SendStreamWriter(func(w *bufio.Writer) {
        for i := 0; ; i++ {
            fmt.Fprintf(w, "data: message %d\n\n", i)
            if err := w.Flush(); err != nil {
                return // client disconnected
            }
            time.Sleep(time.Second)
        }
    })
})

WebSockets are provided by the external gofiber/contrib/websocket package; check c.IsWebSocket() to detect an upgrade handshake.

Streaming a large response body with c.SendStream(reader, size) reads and writes asynchronously after the handler returns — pass c.Context() (not c) to anything that spawns goroutines.


14. Interoperating with net/http

Fiber's router accepts standard-library handlers directly — no wrapper needed:

httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("served by net/http"))
})

app.Get("/legacy", httpHandler) // works as-is

It also adapts Express-style handlers that take fiber.Req and fiber.Res:

app.Get("/", func(req fiber.Req, res fiber.Res) error {
    return res.SendString("Hello from Express-style handlers!")
})

app.Use(func(req fiber.Req, res fiber.Res, next func() error) error {
    if req.IP() == "192.168.1.254" {
        return res.SendStatus(fiber.StatusForbidden)
    }
    return next()
})

To convert entire applications or reuse net/http middleware chains in both directions, use the adaptor middleware.

Note

Adapted net/http handlers run with standard-library semantics — they do not get fiber.Ctx features and pay a small compatibility overhead. Native func(c fiber.Ctx) error handlers are the fastest path.


15. How It Works

A few concepts explain Fiber's behavior and its performance rules.

Built on fasthttp. Fiber is a layer over fasthttp, not Go's net/http. fasthttp avoids per-request allocations by reusing request, response, and context objects from pools. Fiber exposes the underlying objects when you need them: c.RequestCtx(), c.Request(), c.Response(), app.Server(), and app.Handler().

Context reuse is why the "valid only in the handler" rule exists. Because the Ctx (and the fasthttp RequestCtx it wraps) is recycled for the next request, any slice or string it hands you may be overwritten once your handler returns. Copy values you need to keep, or enable Immutable.

fiber.Ctx and context.Context. Ctx implements context.Context, but Deadline(), Done(), and Err() are no-ops due to a fasthttp limitation. To propagate cancellation into goroutines, call c.Context() to get a real context.Context (set one first with c.SetContext() if you need values/deadlines in it).

Routing. Routes are compiled into a tree at registration time. Adding or removing routes at runtime is possible (app.RebuildTree(), app.RemoveRoute()) but is not thread-safe and is meant for development only.

For the full architecture, configuration reference, and internals, see the Fiber Admin Guide.


16. Migrating from v2

If you are porting a Fiber v2 app, these are the changes you will hit first:

  • Import path: github.com/gofiber/fiber/v2 → github.com/gofiber/fiber/v3.
  • Handler signature: the context is now a value type. func(c *fiber.Ctx) error → func(c fiber.Ctx) error.
  • Go version: v3 requires Go 1.25+.
  • Binding: the old c.BodyParser(&v), c.QueryParser(&v), etc. are replaced by the unified c.Bind() API — c.Bind().Body(&v), c.Bind().Query(&v), c.Bind().All(&v).

Caution

There is a community-reported migration pitfall: an automated v2→v3 conversion that rewrites c.BodyParser(c) into c.Bind().Body(c) — passing the context instead of a destination struct — can cause an out-of-memory loop. After any automated migration, verify that every Bind() call passes a pointer to your target struct, not c.


17. Troubleshooting

Error: package github.com/gofiber/fiber/v3 is not in std

Cause: Fiber is not yet downloaded, or your go.mod does not require it. Solution: Run go get -u github.com/gofiber/fiber/v3 inside your module, then go mod tidy. Prevention: Always go mod init before adding dependencies.

Build error: note: module requires Go 1.25

Cause: Your Go toolchain is older than Fiber v3's minimum. Solution: Upgrade Go to 1.25 or newer from go.dev/dl and re-run go build. Prevention: Check go version before starting a v3 project.

Type error: cannot use func(c *fiber.Ctx)... as fiber.Handler

Cause: You wrote a v2-style handler with a pointer receiver *fiber.Ctx. Solution: Change the signature to take a value: func(c fiber.Ctx) error. Prevention: In v3 the context is always passed by value as fiber.Ctx.

My CORS requests still fail in the browser

Cause: Either CORS middleware is not mounted before your routes, or you set AllowCredentials: true together with the "*" origin wildcard (Fiber rejects this combination). Solution: Mount cors.New(...) with app.Use(...) early, and list explicit origins in AllowOrigins when using credentials. Prevention: Never pair credentials with a wildcard origin.

c.IP() returns the proxy's IP, not the client's

Cause: Your app is behind a reverse proxy but TrustProxy is not enabled, so Fiber returns the TCP remote IP. Solution: Set TrustProxy: true, a TrustProxyConfig listing your proxy IPs/ranges, and ProxyHeader: fiber.HeaderXForwardedFor. Setting ProxyHeader alone has no effect. Prevention: Configure proxy trust whenever you deploy behind Nginx, Traefik, a load balancer, or a CDN.

A value read from c is garbled or changes unexpectedly

Cause: You stored a value returned by c.Params()/Query()/Body()/Get() and used it after the handler returned, or passed c to a goroutine. The context was recycled for another request. Solution: Copy the value (e.g. strings.Clone(c.Params("id"))) before keeping it, or set Immutable: true in the config. For goroutines, pass c.Context(), not c. Prevention: Treat everything from c as valid only inside the handler.

Panic crashes the whole server

Cause: No recover middleware is installed; Fiber does not catch panics by default. Solution: Add app.Use(recover.New()) as one of your first middleware. Prevention: Always mount recover in production apps.

Rate limiter / session counts reset or are inconsistent

Cause: You enabled prefork, and the middleware uses the default in-memory store, which is per-process and not shared across the spawned workers. Solution: Use an external store (e.g. a gofiber/storage Redis driver) or app.SharedState(), or disable prefork. Prevention: Never rely on in-process memory for shared state under prefork.

Comma-separated query array binds as one element

Cause: EnableSplittingOnParsers is off (the default), so ?colors=red,blue is a single value. Solution: Either enable EnableSplittingOnParsers: true, or use repeated keys (?colors=red&colors=blue) / bracket notation (?colors[]=red&colors[]=blue). Prevention: Prefer repeated keys, which work regardless of config.


18. FAQ

Q: Is Fiber based on net/http? No. Fiber is built on fasthttp, a separate high-performance HTTP engine. That is the source of both its speed and its "values are valid only in the handler" rule. You can still interoperate with net/http handlers — the router accepts them directly.

Q: Why is my response data corrupted when I use it in a goroutine? Because fiber.Ctx is reused across requests. Copy any value you need outside the handler, and pass c.Context() (not c) into goroutines.

Q: Which Go version do I need? Go 1.25 or higher for Fiber v3.

Q: How do I read JSON into a struct? c.Bind().Body(&v) (it auto-detects JSON from Content-Type), or c.Bind().JSON(&v) explicitly. Tag fields with json:"...".

Q: How do I validate input? Fiber has no built-in validator. Implement the StructValidator interface (commonly with go-playground/validator) and set it as StructValidator in the config; binding then validates automatically.

Q: How do I serve static files? Mount static.New("./public") on a wildcard route, e.g. app.Get("/*", static.New("./public")). For a single file in a handler, use c.SendFile(...).

Q: How do I return a specific status code? return c.SendStatus(code) for a bare status, or return c.Status(code).JSON(...) / .SendString(...) to set the status and a body.

Q: How do I handle errors in one place? Return errors from your handlers and set a custom ErrorHandler in the config. Use fiber.NewError(code, msg) to attach a status code.

Q: How do I add a template engine? Install one from gofiber/template, set it as Views in the config, and call c.Render(name, data).

Q: Does Fiber support WebSockets? Yes, via the external gofiber/contrib/websocket package. Detect upgrades with c.IsWebSocket().

Q: How do I run multiple processes for more throughput? Enable prefork: app.Listen(":8080", fiber.ListenConfig{EnablePrefork: true}). Remember that in-process state is not shared between workers.

Q: Where is the full configuration reference? The complete fiber.Config and ListenConfig field reference, plus deployment, security hardening, and internals, is in the Fiber Admin Guide.