Limiter
December 5, 2025 ยท View on GitHub
A high-performance rate limiting middleware supporting multiple Go web frameworks (Fiber, Gin, Echo, Chi, and standard library) with Redis and in-memory storage, implementing multiple rate-limiting algorithms.
Table of Contents
- Features
- Installation
- Usage
- Basic Example
- With Redis
- Configuration Options
- Response Headers
- Algorithms
- Examples
- Contributing
- License
Features
- ๐ Multiple Algorithms: Token Bucket, Sliding Window, and Fixed Window
- ๐ฅ๏ธ Multi-Framework Support: Fiber, Gin, Echo, Chi, and standard library
- ๐พ Storage Options: Redis (for distributed systems) and in-memory (for single-instance)
- โก High Performance: Minimal overhead with efficient algorithms
- ๐ง Customizable: Flexible key generation and response handling
- ๐ RFC Compliance: Standard
RateLimitheaders (RFC 6585)
Installation
go get github.com/NarmadaWeb/limiter/v2
Usage
The limiter supports multiple web frameworks. Choose the appropriate middleware method for your framework:
- Fiber:
l.FiberMiddleware(config) - Gin:
l.GinMiddleware(config) - Echo:
l.EchoMiddleware(config) - StdLib:
l.StdLibMiddleware(config)(works with Chi, Gorilla Mux, etc.)
Basic Example (Fiber)
package main
import (
"time"
"github.com/gofiber/fiber/v2"
"github.com/NarmadaWeb/limiter/v2"
)
func main() {
app := fiber.New()
// Initialize with default in-memory store
limiterCfg := limiter.Config{
MaxRequests: 100,
Window: 1 * time.Minute,
Algorithm: "sliding-window",
}
l, err := limiter.New(limiterCfg)
if err != nil {
panic(err)
}
app.Use(l.FiberMiddleware(limiter.FiberConfig{}))
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Listen(":3000")
}
With Redis (Fiber)
package main
import (
"time"
"github.com/NarmadaWeb/limiter/v2"
"github.com/gofiber/fiber/v2"
"github.com/redis/go-redis/v9"
)
func main() {
app := fiber.New()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
limiterCfg := limiter.Config{
RedisClient: rdb,
MaxRequests: 200,
Window: 5 * time.Minute,
Algorithm: "token-bucket",
}
l, err := limiter.New(limiterCfg)
if err != nil {
panic(err)
}
app.Use(l.FiberMiddleware(limiter.FiberConfig{}))
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello with Redis!")
})
app.Listen(":3000")
}
Gin Framework
package main
import (
"time"
"github.com/NarmadaWeb/limiter/v2"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
limiterCfg := limiter.Config{
MaxRequests: 100,
Window: 1 * time.Minute,
Algorithm: "sliding-window",
}
l, err := limiter.New(limiterCfg)
if err != nil {
panic(err)
}
r.Use(l.GinMiddleware(limiter.GinConfig{}))
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Hello from Gin!"})
})
r.Run(":8080")
}
Echo Framework
package main
import (
"net/http"
"time"
"github.com/NarmadaWeb/limiter/v2"
"github.com/labstack/echo/v4"
)
func main() {
e := echo.New()
limiterCfg := limiter.Config{
MaxRequests: 100,
Window: 1 * time.Minute,
Algorithm: "sliding-window",
}
l, err := limiter.New(limiterCfg)
if err != nil {
panic(err)
}
e.Use(l.EchoMiddleware(limiter.EchoConfig{}))
e.GET("/", func(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"message": "Hello from Echo!"})
})
e.Logger.Fatal(e.Start(":8080"))
}
Standard Library / Chi Router
package main
import (
"encoding/json"
"net/http"
"time"
"github.com/NarmadaWeb/limiter/v2"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
limiterCfg := limiter.Config{
MaxRequests: 100,
Window: 1 * time.Minute,
Algorithm: "sliding-window",
}
l, err := limiter.New(limiterCfg)
if err != nil {
panic(err)
}
r.Use(l.StdLibMiddleware(limiter.StdLibConfig{}))
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"message": "Hello from Chi!"})
})
http.ListenAndServe(":8080", r)
}
Configuration Options
Core Configuration
| Option | Type | Description |
|---|---|---|
RedisClient | *redis.Client | Redis client instance (optional) |
RedisURL | string | Redis connection URL (alternative to RedisClient) |
MaxRequests | int | Maximum allowed requests per window |
Window | time.Duration | Duration of the rate limit window (e.g., 1*time.Minute) |
Algorithm | string | Rate limiting algorithm (token-bucket, sliding-window, fixed-window) |
Framework-Specific Configuration
Each framework has its own configuration struct with framework-specific handlers:
FiberConfig
| Option | Type | Description |
|---|---|---|
KeyGenerator | func(*fiber.Ctx) string | Custom function to generate rate limit keys (default: client IP) |
SkipSuccessful | bool | Don't count successful requests (status < 400) |
LimitReachedHandler | fiber.Handler | Custom handler when limit is reached |
ErrorHandler | func(*fiber.Ctx, error) error | Custom error handler for storage/configuration errors |
GinConfig
| Option | Type | Description |
|---|---|---|
KeyGenerator | func(*gin.Context) string | Custom function to generate rate limit keys (default: client IP) |
SkipSuccessful | bool | Don't count successful requests (status < 400) |
LimitReachedHandler | func(*gin.Context) | Custom handler when limit is reached |
ErrorHandler | func(*gin.Context, error) | Custom error handler for storage/configuration errors |
EchoConfig
| Option | Type | Description |
|---|---|---|
KeyGenerator | func(echo.Context) string | Custom function to generate rate limit keys (default: real IP) |
SkipSuccessful | bool | Don't count successful requests (status < 400) |
| LimitReachedHandler | func(echo.Context) error | Custom handler when limit is reached |
| ErrorHandler | func(echo.Context, error) error | Custom error handler for storage/configuration errors |
StdLibConfig
| Option | Type | Description |
|---|---|---|
KeyGenerator | func(*http.Request) string | Custom function to generate rate limit keys (default: client IP) |
SkipSuccessful | bool | Don't count successful requests (status < 400) |
LimitReachedHandler | http.HandlerFunc | Custom handler when limit is reached |
ErrorHandler | func(http.ResponseWriter, *http.Request, error) | Custom error handler for storage/configuration errors |
Response Headers
The middleware adds these standard headers to responses:
X-RateLimit-Limit: Maximum requests allowedX-RateLimit-Remaining: Remaining requests in current windowX-RateLimit-Reset: Unix timestamp when limit resetsRateLimit-Policy: Formal policy description
Algorithms
-
Token Bucket
- Smooth bursting allowed
- Gradually refills tokens at steady rate
- Good for evenly distributed loads
-
Sliding Window
- Precise request counting
- Tracks exact request timestamps
- Prevents bursts at window edges
-
Fixed Window
-
Simple implementation
-
Counts requests per fixed interval
-
May allow bursts at window boundaries
-
Examples
See the examples directory for complete implementations for all supported frameworks:
Fiber Examples
-
Basic - Simple rate limiting with in-memory storage
-
Redis - Distributed rate limiting using Redis
-
Multiple Limiters - Using different rate limiters for different routes
-
Error Handling - Custom error and rate limit exceeded handlers
-
Custom Key - Custom key generation for rate limiting buckets
Gin Examples
- Gin Basic - Rate limiting with Gin framework
Echo Examples
- Echo Basic - Rate limiting with Echo framework
Standard Library Examples
- StdLib/Chi - Rate limiting with standard library and Chi router
Contributing
We welcome contributions! Please see our CONTRIBUTING.md for guidelines.
License
MIT ยฉ NarmadaWeb - See LICENSE for details.