Limiter

December 5, 2025 ยท View on GitHub

Go Report Card License: MIT

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

  • ๐Ÿš€ 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 RateLimit headers (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

OptionTypeDescription
RedisClient*redis.ClientRedis client instance (optional)
RedisURLstringRedis connection URL (alternative to RedisClient)
MaxRequestsintMaximum allowed requests per window
Windowtime.DurationDuration of the rate limit window (e.g., 1*time.Minute)
AlgorithmstringRate limiting algorithm (token-bucket, sliding-window, fixed-window)

Framework-Specific Configuration

Each framework has its own configuration struct with framework-specific handlers:

FiberConfig

OptionTypeDescription
KeyGeneratorfunc(*fiber.Ctx) stringCustom function to generate rate limit keys (default: client IP)
SkipSuccessfulboolDon't count successful requests (status < 400)
LimitReachedHandlerfiber.HandlerCustom handler when limit is reached
ErrorHandlerfunc(*fiber.Ctx, error) errorCustom error handler for storage/configuration errors

GinConfig

OptionTypeDescription
KeyGeneratorfunc(*gin.Context) stringCustom function to generate rate limit keys (default: client IP)
SkipSuccessfulboolDon't count successful requests (status < 400)
LimitReachedHandlerfunc(*gin.Context)Custom handler when limit is reached
ErrorHandlerfunc(*gin.Context, error)Custom error handler for storage/configuration errors

EchoConfig

OptionTypeDescription
KeyGeneratorfunc(echo.Context) stringCustom function to generate rate limit keys (default: real IP)
SkipSuccessfulboolDon'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

OptionTypeDescription
KeyGeneratorfunc(*http.Request) stringCustom function to generate rate limit keys (default: client IP)
SkipSuccessfulboolDon't count successful requests (status < 400)
LimitReachedHandlerhttp.HandlerFuncCustom handler when limit is reached
ErrorHandlerfunc(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 allowed
  • X-RateLimit-Remaining: Remaining requests in current window
  • X-RateLimit-Reset: Unix timestamp when limit resets
  • RateLimit-Policy: Formal policy description

Algorithms

  1. Token Bucket

    • Smooth bursting allowed
    • Gradually refills tokens at steady rate
    • Good for evenly distributed loads
  2. Sliding Window

    • Precise request counting
    • Tracks exact request timestamps
    • Prevents bursts at window edges
  3. 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

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.