Session
The Session middleware adds session management to Fiber apps through the Storage package, which offers a unified interface for multiple databases. By default, sessions live in memory, but you can plug in any storage backend.
Table of Contentsβ
- Quick Start
- Usage Patterns
- Session Security
- Session ID Extractors
- Configuration
- Migration Guide
- API Reference
- Examples
Quick Startβ
import (
"fmt"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/session"
)
// Basic usage
app.Use(session.New())
app.Get("/", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Get and update visits count
var visits int
if v := sess.Get("visits"); v != nil {
// Use type assertion with an ok check to prevent a panic
if vInt, ok := v.(int); ok {
visits = vInt
}
}
visits++
sess.Set("visits", visits)
return c.SendString(fmt.Sprintf("Visits: %d", visits))
})
Production Configurationβ
import (
"time"
"github.com/gofiber/fiber/v3/extractors"
"github.com/gofiber/storage/redis/v3"
)
storage := redis.New(redis.Config{
Host: "localhost",
Port: 6379,
})
app.Use(session.New(session.Config{
Storage: storage,
CookieSecure: true, // HTTPS only
CookieHTTPOnly: true, // Prevent XSS
CookieSameSite: "Lax", // CSRF protection
IdleTimeout: 30 * time.Minute, // Session timeout
AbsoluteTimeout: 24 * time.Hour, // Maximum session life
Extractor: extractors.FromCookie("__Host-session_id"),
}))
Notes:
- AbsoluteTimeout must be greater than or equal to IdleTimeout; otherwise, the middleware panics during configuration.
- CookieSameSite accepts
"Disabled","Lax","Strict", or"None"case-insensitively; other values panic during configuration. - If CookieSameSite is set to
"Disabled", the cookie omits the SameSite attribute and preservesCookieSecure. - If CookieSameSite is set to
"None", the middleware automatically forcesCookieSecure=truewhen setting the cookie.
Usage Patternsβ
Middleware Pattern (Recommended)β
This pattern automatically manages the session lifecycle and is recommended for most applications.
// Setup middleware
app.Use(session.New())
// Use in handlers
app.Post("/login", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Session is automatically saved when handler returns
sess.Set("user_id", 123)
sess.Set("authenticated", true)
return c.Redirect().To("/dashboard")
})
Benefits:
- Automatic session saving
- Automatic resource cleanup
- No manual lifecycle management
- Thread-safe operations
Store Pattern (Advanced)β
Use the store pattern for background tasks or when you need direct access to sessions.
import (
"context"
"log"
"time"
)
store := session.NewStore()
// In background tasks
func backgroundTask(sessionID string) {
sess, err := store.GetByID(context.Background(), sessionID)
if err != nil {
return
}
defer sess.Release() // Important: Manual cleanup required
// Modify session
sess.Set("last_task", time.Now().Unix())
// Manual save required
if err := sess.Save(); err != nil {
log.Printf("Failed to save session: %v", err)
}
}
Requirements:
- Must call
sess.Release()when done - Must call
sess.Save()to persist changes - Handle errors manually
Session Securityβ
Authentication Flowβ
Understanding session lifecycle during authentication is crucial for security.
Basic Login/Logoutβ
app.Post("/login", func(c fiber.Ctx) error {
sess := session.FromContext(c)
email := c.FormValue("email")
password := c.FormValue("password")
// Simple credential validation (use proper authentication in production)
// Important: Regenerate the session ID to prevent fixation
// This changes the session ID while preserving existing data
if err := sess.Regenerate(); err != nil {
return c.Status(500).SendString("Session error")
}
// Add authentication data to existing session
sess.Set("user_id", 1)
sess.Set("authenticated", true)
return c.Redirect().To("/dashboard")
}
return c.Status(401).SendString("Invalid credentials")
})
app.Post("/logout", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Complete session reset (clears all data + new session ID)
if err := sess.Reset(); err != nil {
return c.Status(500).SendString("Session error")
}
return c.Redirect().To("/")
})
Cart Preservation During Loginβ
app.Post("/login", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Validate credentials (implement your own validation)
email := c.FormValue("email")
password := c.FormValue("password")
if !isValidUser(email, password) {
return c.Status(401).JSON(fiber.Map{"error": "Invalid credentials"})
}
// Important: Regenerate the session ID to prevent fixation
// This changes the session ID while preserving existing data
if err := sess.Regenerate(); err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Session error"})
}
// Add authentication data to existing session
sess.Set("user_id", getUserID(email))
sess.Set("authenticated", true)
sess.Set("login_time", time.Now().Unix())
return c.JSON(fiber.Map{"status": "logged in"})
})
Security Methods Comparisonβ
| Method | Session ID | Session Data | Use Case |
|---|---|---|---|
Regenerate() | β Changes | β Preserved | Login, privilege escalation |
Reset() | β Changes | β Cleared | Logout, security breach |
Destroy() | βͺ Unchanged, cookie expired | β Cleared | End the session without issuing a new ID |
These methods, store.Delete and store.Reset also hold against other requests of the same session that are still running: a request that loaded the session before its ID was deleted neither stores it again nor sends its cookie when it finishes, and its changes to the session are dropped. This holds when that request calls Regenerate() as well: the ID still changes, so the old one is never written again, but the new one is neither stored nor sent. Of two requests that rotate one session at the same time, only the first stores and sends a new ID, and a login that rotates a session a parallel logout just ended is dropped too. A request that left the session unchanged does not write it back over a change another request stored meanwhile, so a role taken away while it ran stays taken away; reading a value, also one it could change in place such as a slice or a map, does not count as a change, and neither does the CSRF middleware behind the session middleware finding its token still fresh. That holds when such a request calls Regenerate() too: the rotated session carries the change the other request stored, not the snapshot this one loaded. Of two requests that both change the session with Set, Delete or Reset, the one that saves last stores its whole session. This is guaranteed within one process. Processes that share an external Storage are not coordinated, because fiber.Storage has no conditional write, so a request still running on another instance can store a session it loaded before the delete. Where a logout must hold across instances, also keep a server-side revocation marker (for example, a per-user "sessions valid after" timestamp) that every request checks.
Common Security Mistakesβ
β Session Fixation Vulnerability:
// DANGEROUS: Keeping same session ID after login
app.Post("/login", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Validate user...
sess.Set("user_id", userID) // Attacker can hijack this session!
return c.Redirect().To("/dashboard")
})
β Secure Implementation:
// SECURE: Always regenerate session ID after authentication
app.Post("/login", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Validate user...
if err := sess.Regenerate(); err != nil { // Prevents session fixation
return err
}
sess.Set("user_id", userID)
return c.Redirect().To("/dashboard")
})
Authentication Middlewareβ
This is a basic example of an authentication middleware that checks if a user is logged in before accessing protected routes.
// Authentication check middleware
func RequireAuth(c fiber.Ctx) error {
sess := session.FromContext(c)
if sess == nil {
return c.Redirect().To("/login")
}
// Check if user is authenticated
if sess.Get("authenticated") != true {
return c.Redirect().To("/login")
}
return c.Next()
}
// Usage
app.Use("/dashboard", RequireAuth)
app.Use("/admin", RequireAuth)
Automatic Session Expirationβ
Sessions automatically expire based on your configuration:
app.Use(session.New(session.Config{
IdleTimeout: 30 * time.Minute, // Auto-expire after 30 min of inactivity
AbsoluteTimeout: 24 * time.Hour, // Force expire after 24 hours regardless of activity
}))
How it works:
IdleTimeout: Storage automatically removes sessions after inactivity period- Any route that uses the middleware will reset the idle timer
- Calling
sess.Save()will also reset the idle timer
AbsoluteTimeout: Sessions are forcibly expired after maximum duration- No manual cleanup required - the storage layer handles this
Session ID Extractorsβ
This middleware uses the shared extractors module for session ID extraction. See the Extractors Guide for more details.
Built-in Extractorsβ
// Cookie-based (recommended for web apps)
extractors.FromCookie("session_id")
// Header-based (recommended for APIs)
extractors.FromHeader("X-Session-ID")
// Authorization header (read-only)
extractors.FromAuthHeader("Bearer")
// Form data
extractors.FromForm("session_id")
// URL query parameter
extractors.FromQuery("session_id")
// URL path parameter
extractors.FromParam("id")
Session Response Behavior:
- Cookie extractors: set cookie in the response
- Header extractors (non-Authorization): set header in the response
- Authorization header, Query, Form, Param, Custom: read-only (no response values are set)
Multiple Sources with Fallbackβ
app.Use(session.New(session.Config{
Extractor: extractors.Chain(
extractors.FromCookie("session_id"), // Try cookie first
extractors.FromHeader("X-Session-ID"), // Then header
extractors.FromQuery("session_id"), // Finally query
),
}))
Response Behavior with Chained Extractors:
Only cookie and non-Authorization header extractors contribute to response setting. Others are read-only.
- Cookie + Header (non-Auth) extractors: both cookie and header are set
- Only Cookie extractors: only cookie is set
- Only Header (non-Auth) extractors: only header is set
- Any mix that includes Authorization/Query/Form/Param/Custom: those sources are read-only
These rules describe a request that carries no session ID, for example a first visit. For an existing session, the ID is written back only through the extractor that supplied it, and only when that extractor is a cookie or a non-Authorization header. An ID that arrived through a read-only source (Authorization header, Query, Form, Param, or Custom) is never copied into a cookie or header, which prevents session fixation. When the middleware issues a new ID for a request that carried one, because Regenerate() or Reset() was called or because the supplied ID is not in the storage, the new ID goes back through the same cookie or header, or through every cookie and header extractor of the chain when the old ID came from a read-only source.
// This will set both cookie and header in response
extractors.Chain(
extractors.FromCookie("session_id"),
extractors.FromHeader("X-Session-ID"),
)
// This will set only cookie in response
extractors.Chain(
extractors.FromCookie("session_id"),
extractors.FromQuery("session_id"), // Ignored for response
)
// This will set nothing in response (read-only mode)
extractors.Chain(
extractors.FromQuery("session_id"),
extractors.FromForm("session_id"),
)
Custom Extractors (Session-specific)β
Prefer the helper constructors from the extractors module. See the Extractors Guide for the full API; below are session-specific examples and notes.
// Authorization Bearer tokens (read-only for sessions)
// The session middleware will NOT set Authorization back in the response.
app.Use(session.New(session.Config{
Extractor: extractors.FromAuthHeader("Bearer"),
}))
// Custom read-only header via FromCustom (read-only for sessions)
app.Use(session.New(session.Config{
Extractor: extractors.FromCustom("X-Custom-Session", func(c fiber.Ctx) (string, error) {
v := c.Get("X-Custom-Session")
if v == "" { return "", extractors.ErrNotFound }
return v, nil
}),
}))
Return extractors.ErrNotFound when the request carries no session ID, so the visitor gets a new session. Any other error from an extractor is not treated as a new visitor: it goes to ErrorHandler, which answers 500 by default, or is returned by store.Get. This lets a custom or chain-level Extract that validates the ID refuse a forged one. A validating child inside a built-in extractors.Chain cannot: the chain keeps walking after an error, and a later child's value, or its ErrNotFound, replaces the refusal, so put the validation in the chain-level Extract or use the validating extractor alone. Such an Extract must return the ID unchanged, because the same value is written back and looked up in the storage.
Configurationβ
Storage Optionsβ
import (
"github.com/gofiber/storage/redis/v3"
"github.com/gofiber/storage/postgres/v3"
)
// Redis (recommended for production)
redisStorage := redis.New(redis.Config{
Host: "localhost",
Port: 6379,
Password: "",
Database: 0,
})
// PostgreSQL
pgStorage := postgres.New(postgres.Config{
Host: "localhost",
Port: 5432,
Database: "sessions",
Username: "user",
Password: "pass",
})
app.Use(session.New(session.Config{
Storage: redisStorage,
}))
A session is stored under session:<session ID>, so a session ID sent by a client cannot name an entry another middleware keeps in the same Storage. store.GetByID and store.Delete take the bare session ID. store.Reset clears the whole Storage, the entries of other middleware included. Sessions stored by an earlier version under the bare ID are not found after the upgrade, so users sign in again; those entries expire on their own in an external Storage.
Production Security Settingsβ
import (
"log"
"time"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/utils/v2"
"github.com/gofiber/fiber/v3/extractors"
)
app.Use(session.New(session.Config{
// Storage
Storage: redisStorage,
// Security
CookieSecure: true, // HTTPS only (required in production)
CookieHTTPOnly: true, // No JavaScript access (prevents XSS)
CookieSameSite: "Lax", // CSRF protection
// Session Management
IdleTimeout: 30 * time.Minute, // Inactivity timeout
AbsoluteTimeout: 24 * time.Hour, // Maximum session duration
// Cookie Settings
CookiePath: "/",
// CookieDomain stays empty: browsers reject a __Host- cookie that sets a Domain
CookieSessionOnly: false, // Persist across browser restarts
// Session ID
Extractor: extractors.FromCookie("__Host-session_id"),
KeyGenerator: utils.SecureToken,
// Error Handling
ErrorHandler: func(c fiber.Ctx, err error) {
log.Printf("Session error: %v", err)
// Write the response: when the session cannot be loaded, the request is
// not passed on, so nothing else answers it.
if sendErr := c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Session error"}); sendErr != nil {
log.Printf("Failed to send error response: %v", sendErr)
}
},
}))
A custom ErrorHandler has to write the response itself. When the session cannot be loaded from the storage, the request is not passed on, so a handler that only logs answers with an empty 200. When the session cannot be saved, the route handler has already run: the status and body that ErrorHandler writes replace its own, while its headers stay, and a handler that only logs leaves that response, usually a success, for a session the storage did not keep.
Custom Typesβ
Session data supports basic Go types by default:
string,int,int8,int16,int32,int64uint,uint8,uint16,uint32,uint64bool,float32,float64[]byte,complex64,complex128any
For custom types (structs, maps, slices), you must register them for encoding/decoding:
import "fmt"
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Role string `json:"role"`
}
// Method 1: Using NewWithStore
func main() {
app := fiber.New()
sessionMiddleware, store := session.NewWithStore()
store.RegisterType(User{}) // Register custom type
app.Use(sessionMiddleware)
app.Get("/", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Use custom type
sess.Set("user", User{ID: 123, Name: "John", Role: "admin"})
user, ok := sess.Get("user").(User)
if ok {
return c.JSON(fiber.Map{"user": user.Name, "role": user.Role})
}
return c.SendString("No user found")
})
app.Listen(":3000")
}
// Method 2: Using separate store
store := session.NewStore()
store.RegisterType(User{})
app.Use(session.New(session.Config{
Store: store,
}))
// Usage in handlers
sess.Set("user", User{ID: 123, Name: "John", Role: "admin"})
user, ok := sess.Get("user").(User)
if ok {
fmt.Printf("User: %s (Role: %s)", user.Name, user.Role)
}
Important Notes:
- Custom types must be registered before using them in sessions
- Registration must happen during application startup
- All instances of the application must register the same types
- Types are encoded using Go's
gobpackage time.Timeis a struct, so it needsstore.RegisterType(time.Time{})as well. The store registers it by itself only whenAbsoluteTimeoutis greater than zero. Saving a session that holds an unregisteredtime.Timefails, and the middleware answers with itsErrorHandler(500by default). Storetime.Now().Unix()when a plain timestamp is enough- Reading a struct back out with
Getmakes the save re-encode, even for an all-scalar struct likeUser, because every struct is treated as possibly sharing memory with the session. A request that only reads scalars ortime.Timevalues writes the stored bytes back unchanged
Migration Guideβ
v2 to v3 Breaking Changesβ
- Function Signature:
session.New()now returns middleware handler, not store - Session ID Extraction:
KeyLookupreplaced withExtractorfunctions - Lifecycle Management: Manual
Release()required for store pattern - Timeout Handling:
Expirationsplit intoIdleTimeoutandAbsoluteTimeout
Migration Examplesβ
v2 Code:
store := session.New(session.Config{
KeyLookup: "cookie:session_id",
})
app.Get("/", func(c fiber.Ctx) error {
sess, err := store.Get(c)
if err != nil {
return err
}
// Session automatically saved and released
sess.Set("key", "value")
return nil
})
v3 Middleware Pattern (Recommended):
app.Use(session.New(session.Config{
Extractor: extractors.FromCookie("session_id"),
}))
app.Get("/", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Session automatically saved and released
sess.Set("key", "value")
return nil
})
v3 Store Pattern (Advanced):
store := session.NewStore(session.Config{
Extractor: extractors.FromCookie("session_id"),
})
app.Get("/", func(c fiber.Ctx) error {
sess, err := store.Get(c)
if err != nil {
return err
}
defer sess.Release() // Manual cleanup required
sess.Set("key", "value")
return sess.Save() // Manual save required
})
KeyLookup to Extractor Migrationβ
| v2 KeyLookup | v3 Extractor |
|---|---|
"cookie:session_id" | extractors.FromCookie("session_id") |
"header:X-Session-ID" | extractors.FromHeader("X-Session-ID") |
"query:session_id" | extractors.FromQuery("session_id") |
"form:session_id" | extractors.FromForm("session_id") |
"cookie:sid,header:X-Sid" | extractors.Chain(extractors.FromCookie("sid"), extractors.FromHeader("X-Sid")) |
API Referenceβ
Constructors and Helpersβ
func New(config ...Config) fiber.Handler
func NewWithStore(config ...Config) (fiber.Handler, *Store)
func NewStore(config ...Config) *Store
func FromContext(ctx any) *Middleware
func DefaultErrorHandler(c fiber.Ctx, err error)
NewWithStore returns the middleware together with the Store it uses, for example to call RegisterType. FromContext returns nil when the request has no session middleware.
Middleware Methods (Recommended)β
sess := session.FromContext(c)
// Data operations
sess.Get(key any) any
sess.Set(key, value any)
sess.Delete(key any)
sess.Keys() []any
// Session management
sess.ID() string
sess.Fresh() bool
sess.Regenerate() error // Change ID, keep data
sess.Reset() error // Change ID, clear data
sess.Destroy() error // Delete from storage, expire the cookie
// Context-aware variants propagate cancellation/deadlines to storage I/O.
// Pass a context.Context to control timeouts; a nil context is treated as
// context.Background().
sess.RegenerateWithContext(ctx context.Context) error
sess.ResetWithContext(ctx context.Context) error
sess.DestroyWithContext(ctx context.Context) error
// Store access
sess.Store() *session.Store
FromContext accepts a fiber.CustomCtx, fiber.Ctx, a *fasthttp.RequestCtx, or a context.Context.
The returned *session.Middleware is only valid while the request is being handled. A context you kept from c.Context() (with PassLocalsToContext enabled) stops resolving it when the request ends: FromContext returns nil for that context from then on, so a goroutine that outlives the handler cannot reach the session, and never another request's. Do not hand the pointer itself to such a goroutine. Pass it the session ID and use the Store Pattern instead.
A handler the Timeout middleware gave up on keeps its *session.Middleware and its session until it returns, and no other request is handed them. The session of such a request is not saved: the timeout response is already fixed and cannot carry a cookie, and the handler may still be changing the data.
A session that was only read is written back to storage without being re-encoded. Get and Keys give that up when the value or key they return could share memory with the session (a map, slice, array, pointer or struct; scalars and time.Time are safe), because the caller could then change it in place without calling Set. Such an edit counts as the request's own change, as a Set does: its whole session then wins as described under Session Security, while a request that only read yields to a change another request stored meanwhile. Calling Set with the edited value says so outright and spares the save a decode of the snapshot.
Store Methodsβ
store := session.NewStore()
// Store operations
store.Get(c fiber.Ctx) (*session.Session, error)
store.GetByID(ctx context.Context, sessionID string) (*session.Session, error)
store.Reset(ctx context.Context) error
store.Delete(ctx context.Context, sessionID string) error
// Type registration
store.RegisterType(User{})
Errorsβ
var (
ErrEmptySessionID = errors.New("session ID cannot be empty")
ErrSessionAlreadyLoadedByMiddleware = errors.New("session already loaded by middleware")
ErrSessionIDNotFoundInStore = errors.New("session ID not found in session store")
)
store.Get(c)returnsErrSessionAlreadyLoadedByMiddlewarewhen the session middleware already loaded the session for the request. Usesession.FromContext(c)there.store.GetByIDandstore.DeletereturnErrEmptySessionIDfor an empty session ID.store.GetByIDreturnsErrSessionIDNotFoundInStorewhen the storage has no such session, or when the session has outlived itsAbsoluteTimeout(it is deleted).
Session Methods (Store Pattern)β
sess, err := store.Get(c)
defer sess.Release() // Required!
// Same methods as middleware (except Store()), plus:
sess.Save() error // Manual save required
sess.SetIdleTimeout(duration) // Per-session timeout
sess.Release() // Manual cleanup required
// Context-aware variants propagate cancellation/deadlines to storage I/O.
// A nil context is treated as context.Background().
sess.DestroyWithContext(ctx context.Context) error
sess.RegenerateWithContext(ctx context.Context) error
sess.ResetWithContext(ctx context.Context) error
sess.SaveWithContext(ctx context.Context) error
Session with Context (timeouts/cancellation)β
The *WithContext variants let you propagate a context.Context to the underlying storage call so that slow or unresponsive backends can be bounded by a deadline or canceled. This mirrors the Storage and SharedState WithContext convention.
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
// Persist the session, bounding the storage write by the deadline.
if err := sess.SaveWithContext(ctx); err != nil {
// deadline exceeded, cancellation, or storage error
}
// Destroy is also covered:
// if err := sess.DestroyWithContext(ctx); err != nil { ... }
A nil context is treated as context.Background(), so sess.SaveWithContext(nil) is equivalent to sess.Save() when no request context is available.
Extractor Functionsβ
// Built-in extractors (import "github.com/gofiber/fiber/v3/extractors")
extractors.FromCookie(key string) extractors.Extractor
extractors.FromHeader(key string) extractors.Extractor
extractors.FromAuthHeader(authScheme string) extractors.Extractor
extractors.FromQuery(key string) extractors.Extractor
extractors.FromForm(key string) extractors.Extractor
extractors.FromParam(key string) extractors.Extractor
extractors.FromCustom(key string, fn func(fiber.Ctx) (string, error)) extractors.Extractor
// Chaining
extractors.Chain(extractors ...extractors.Extractor) extractors.Extractor
Config Propertiesβ
| Property | Type | Description | Default |
|---|---|---|---|
Store | *session.Store | Pre-built session store (use when you need to share/register types) | nil (auto-created) |
Storage | fiber.Storage | Session storage backend (used when creating a store if Store is nil) | memory.New() |
Extractor | extractors.Extractor | Session ID extraction | extractors.FromCookie("session_id") |
KeyGenerator | func() string | Session ID generator | utils.SecureToken |
IdleTimeout | time.Duration | Inactivity timeout | 30 * time.Minute |
AbsoluteTimeout | time.Duration | Maximum session duration | 0 (unlimited) |
CookieSecure | bool | HTTPS only | false |
CookieHTTPOnly | bool | No JavaScript access | false |
CookieSameSite | string | SameSite mode: Disabled, Lax, Strict, or None | "Lax" |
CookiePath | string | Cookie path | "" |
CookieDomain | string | Cookie domain | "" |
CookieSessionOnly | bool | Session cookie | false |
Next | func(fiber.Ctx) bool | Skip middleware when returns true | nil |
ErrorHandler | func(fiber.Ctx, error) | Called when the session cannot be loaded from the storage, in which case the request is not passed on, or cannot be saved after the handler returns. A custom handler has to write the response: one that only logs answers an empty 200 after a load failure and leaves the route handler's response in place after a save failure. The default logs the error and responds with 500 Internal Server Error | DefaultErrorHandler |
Default Configβ
var ConfigDefault = Config{
IdleTimeout: 30 * time.Minute,
KeyGenerator: utils.SecureToken,
Extractor: extractors.FromCookie("session_id"),
CookieSameSite: fiber.CookieSameSiteLaxMode,
}
An unset IdleTimeout, Extractor, KeyGenerator, or CookieSameSite is filled from ConfigDefault. A Storage that is nil becomes an in-memory store when the store is created.
Examplesβ
E-commerce with Cart Persistenceβ
import (
"time"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/session"
"github.com/gofiber/fiber/v3/extractors"
"github.com/gofiber/storage/redis/v3"
)
func main() {
app := fiber.New()
// Session middleware
app.Use(session.New(session.Config{
Storage: redis.New(),
CookieSecure: true,
CookieHTTPOnly: true,
CookieSameSite: "Lax",
IdleTimeout: 30 * time.Minute,
AbsoluteTimeout: 24 * time.Hour,
Extractor: extractors.FromCookie("__Host-cart_session"),
}))
// Add to cart (anonymous user)
app.Post("/cart/add", func(c fiber.Ctx) error {
sess := session.FromContext(c)
cart, _ := sess.Get("cart").([]string)
cart = append(cart, c.FormValue("item_id"))
sess.Set("cart", cart)
return c.JSON(fiber.Map{"items": len(cart)})
})
// Login (preserve session data)
app.Post("/login", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Simple validation (implement proper authentication)
email := c.FormValue("email")
password := c.FormValue("password")
return c.Status(401).JSON(fiber.Map{"error": "Invalid credentials"})
}
// Regenerate session ID for security
// This changes the session ID while preserving existing data
if err := sess.Regenerate(); err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Session error"})
}
sess.Set("user_id", 1)
sess.Set("authenticated", true)
return c.JSON(fiber.Map{"status": "logged in"})
})
// Logout (clear everything)
app.Post("/logout", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Reset clears all data and generates new session ID
if err := sess.Reset(); err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Session error"})
}
return c.JSON(fiber.Map{"status": "logged out"})
})
app.Listen(":3000")
}
// Helper functions (implement these properly in production)
func isValidUser(email, password string) bool {
}
func getUserID(email string) int {
return 1 // Return actual user ID from database
}
API with Header-based Sessionsβ
import (
"time"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/session"
"github.com/gofiber/fiber/v3/extractors"
"github.com/gofiber/storage/redis/v3"
)
func main() {
app := fiber.New()
// API session middleware with header extraction
app.Use(session.New(session.Config{
Storage: redis.New(),
Extractor: extractors.FromHeader("X-Session-Token"),
IdleTimeout: time.Hour,
}))
// API endpoint
app.Post("/api/data", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Track API usage
count, _ := sess.Get("api_calls").(int)
count++
sess.Set("api_calls", count)
sess.Set("last_call", time.Now().Unix())
return c.JSON(fiber.Map{
"data": "some data",
"calls": count,
})
})
app.Listen(":3000")
}
Multi-source Session ID Supportβ
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/session"
"github.com/gofiber/fiber/v3/extractors"
)
func main() {
app := fiber.New()
// Support multiple sources with priority
app.Use(session.New(session.Config{
Extractor: extractors.Chain(
extractors.FromCookie("session_id"), // 1st: Cookie (web)
extractors.FromHeader("X-Session-ID"), // 2nd: Header (API)
extractors.FromQuery("session_id"), // 3rd: Query (fallback)
),
}))
app.Get("/", func(c fiber.Ctx) error {
sess := session.FromContext(c)
// Works with any of the above methods
return c.JSON(fiber.Map{
"session_id": sess.ID(),
"source": "multi-source",
})
})
app.Listen(":3000")
}