Documentation Index
Fetch the complete documentation index at: https://mintlify.com/gofiber/fiber/llms.txt
Use this file to discover all available pages before exploring further.
Limiter middleware for Fiber throttles repeated requests to public APIs, endpoints, or services. It’s useful for preventing abuse, protecting against DDoS attacks, and managing API quotas.
Installation
go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/fiber/v3/middleware/limiter
Signatures
func New(config ...Config) fiber.Handler
Usage
Basic Usage
package main
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/limiter"
"time"
)
func main() {
app := fiber.New()
// 5 requests per minute
app.Use(limiter.New())
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Listen(":3000")
}
Custom Configuration
app.Use(limiter.New(limiter.Config{
Max: 20,
Expiration: 30 * time.Second,
KeyGenerator: func(c fiber.Ctx) string {
return c.Get("x-forwarded-for")
},
LimitReached: func(c fiber.Ctx) error {
return c.Status(fiber.StatusTooManyRequests).SendFile("./toofast.html")
},
}))
Skip Localhost
app.Use(limiter.New(limiter.Config{
Next: func(c fiber.Ctx) bool {
return c.IP() == "127.0.0.1"
},
}))
Sliding Window Algorithm
app.Use(limiter.New(limiter.Config{
Max: 20,
Expiration: 30 * time.Second,
LimiterMiddleware: limiter.SlidingWindow{},
}))
Dynamic Limits
app.Use(limiter.New(limiter.Config{
MaxFunc: func(c fiber.Ctx) int {
// Premium users get higher limits
if isPremiumUser(c) {
return 100
}
return 20
},
Expiration: 1 * time.Minute,
}))
Dynamic Expiration
app.Use(limiter.New(limiter.Config{
Max: 20,
ExpirationFunc: func(c fiber.Ctx) time.Duration {
// Longer window for sensitive endpoints
if c.Path() == "/login" {
return 60 * time.Second
}
return 30 * time.Second
},
}))
Custom Storage (Redis)
import "github.com/gofiber/storage/redis/v3"
store := redis.New(redis.Config{
Host: "127.0.0.1",
Port: 6379,
Database: 0,
})
app.Use(limiter.New(limiter.Config{
Storage: store,
}))
Configuration
Next
func(fiber.Ctx) bool
default:"nil"
Function to skip this middleware when it returns true.
Maximum number of requests allowed within the Expiration window.
MaxFunc
func(fiber.Ctx) int
default:"Returns cfg.Max"
Function to dynamically calculate the maximum requests per window.
KeyGenerator
func(fiber.Ctx) string
default:"c.IP()"
Function to generate custom rate limit keys. Defaults to client IP address.
Expiration
time.Duration
default:"1 * time.Minute"
Time window for counting requests.
ExpirationFunc
func(fiber.Ctx) time.Duration
default:"Returns cfg.Expiration"
Function to dynamically calculate the expiration window.
LimitReached
fiber.Handler
default:"Returns 429 status"
Handler called when a request exceeds the limit.
When true, requests with status code ≥ 400 are not counted.
When true, requests with status code < 400 are not counted.
When true, rate limit headers (X-RateLimit-*, Retry-After) are omitted.
Disables redaction of limiter keys in logs and error messages.
Storage
fiber.Storage
default:"In-memory storage"
Storage backend for persisting rate limit data.
LimiterMiddleware
limiter.Handler
default:"FixedWindow{}"
Algorithm implementation (FixedWindow or SlidingWindow).
Default Configuration
var ConfigDefault = Config{
Max: 5,
Expiration: 1 * time.Minute,
MaxFunc: func(_ fiber.Ctx) int {
return 5
},
KeyGenerator: func(c fiber.Ctx) string {
return c.IP()
},
LimitReached: func(c fiber.Ctx) error {
return c.SendStatus(fiber.StatusTooManyRequests)
},
SkipFailedRequests: false,
SkipSuccessfulRequests: false,
DisableHeaders: false,
DisableValueRedaction: false,
LimiterMiddleware: FixedWindow{},
}
When DisableHeaders is false, the middleware sets these headers:
X-RateLimit-Limit: Maximum requests allowed
X-RateLimit-Remaining: Requests remaining in current window
X-RateLimit-Reset: Unix timestamp when the limit resets
Retry-After: Seconds until the limit resets (only when limit exceeded)
Best Practices
Use Redis for Distributed Systems
import "github.com/gofiber/storage/redis/v3"
store := redis.New(redis.Config{
Host: "127.0.0.1",
Port: 6379,
Database: 0,
Reset: false,
})
app.Use(limiter.New(limiter.Config{
Storage: store,
Max: 100,
Expiration: 1 * time.Minute,
}))
Different Limits for Different Endpoints
// Strict limit for login
app.Post("/login", limiter.New(limiter.Config{
Max: 5,
Expiration: 15 * time.Minute,
}), handleLogin)
// Generous limit for API
app.Use("/api", limiter.New(limiter.Config{
Max: 1000,
Expiration: 1 * time.Hour,
}))
Rate Limit by User ID
app.Use(limiter.New(limiter.Config{
KeyGenerator: func(c fiber.Ctx) string {
// Use user ID if authenticated, otherwise IP
if userID := c.Locals("user_id"); userID != nil {
return fmt.Sprintf("user:%v", userID)
}
return c.IP()
},
}))
Common Patterns
Skip Rate Limiting for Failed Requests
app.Use(limiter.New(limiter.Config{
SkipFailedRequests: true, // Don't count 4xx/5xx responses
Max: 100,
Expiration: 1 * time.Minute,
}))
Custom Error Response
app.Use(limiter.New(limiter.Config{
LimitReached: func(c fiber.Ctx) error {
return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{
"error": "Rate limit exceeded",
"retry_after": 60,
})
},
}))
Tiered Rate Limiting
func getUserTier(c fiber.Ctx) string {
// Determine user tier from context
return c.Locals("tier").(string)
}
app.Use(limiter.New(limiter.Config{
MaxFunc: func(c fiber.Ctx) int {
switch getUserTier(c) {
case "premium":
return 1000
case "standard":
return 100
default:
return 20
}
},
KeyGenerator: func(c fiber.Ctx) string {
userID := c.Locals("user_id")
return fmt.Sprintf("user:%v", userID)
},
}))
Sliding Window Example
// Smoother rate limiting with sliding window
app.Use(limiter.New(limiter.Config{
Max: 20,
Expiration: 30 * time.Second,
LimiterMiddleware: limiter.SlidingWindow{},
}))
The sliding window algorithm calculates the rate as:
weightOfPreviousWindow = previousWindowRequests * (elapsedInCurrentWindow / Expiration)
rate = weightOfPreviousWindow + currentWindowRequests
X-Forwarded-For Support
app.Use(limiter.New(limiter.Config{
KeyGenerator: func(c fiber.Ctx) string {
// Use X-Forwarded-For if behind a proxy
if xff := c.Get("X-Forwarded-For"); xff != "" {
// Get first IP in the chain
ips := strings.Split(xff, ",")
return strings.TrimSpace(ips[0])
}
return c.IP()
},
}))
Testing
# Make requests and check headers
curl -I http://localhost:3000
# Response headers:
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 4
X-RateLimit-Reset: 1234567890
# After exceeding limit:
HTTP/1.1 429 Too Many Requests
Retry-After: 45
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1234567890
Storage Backends
You can use any storage from the Fiber storage package:
- Memory (default, single process)
- Redis
- Memcache
- PostgreSQL
- MySQL
- SQLite
- MongoDB
- And more…
import "github.com/gofiber/storage/sqlite3/v2"
storage := sqlite3.New()
app.Use(limiter.New(limiter.Config{
Storage: storage,
}))