FiberMiddleCoding

Как реализовать rate limiting в Fiber?

Подключите github.com/gofiber/fiber/v2/middleware/limiter с limiter.New(limiter.Config{Max: N, Expiration: time.Minute}). Для распределённого окружения используйте Storage с Redis-бэкендом вместо памяти по умолчанию.

Rate limiting в Fiber

Fiber включает middleware github.com/gofiber/fiber/v2/middleware/limiter, реализующий алгоритм фиксированного окна (fixed window). Для sliding window и более тонкой настройки подключается Redis-хранилище.

Базовый пример (in-memory)

package main

import (
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/limiter"
)

func main() {
	app := fiber.New()

	app.Use(limiter.New(limiter.Config{
		Max:        100,            // максимум запросов
		Expiration: 1 * time.Minute, // за это окно
		KeyGenerator: func(c *fiber.Ctx) string {
			return c.IP() // ключ = IP клиента
		},
		LimitReached: func(c *fiber.Ctx) error {
			return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{
				"error": "rate limit exceeded, try again later",
			})
		},
	}))

	app.Get("/", func(c *fiber.Ctx) error {
		return c.SendString("OK")
	})

	app.Listen(":3000")
}

Распределённое хранилище (Redis)

In-memory хранилище не шарится между несколькими экземплярами приложения. Для горизонтального масштабирования подключите Redis через github.com/gofiber/storage/redis/v3:

import (
	"github.com/gofiber/fiber/v2/middleware/limiter"
	"github.com/gofiber/storage/redis/v3"
)

store := redis.New(redis.Config{
	Host:     "localhost",
	Port:     6379,
	Password: "",
	Database: 0,
})

app.Use(limiter.New(limiter.Config{
	Max:        200,
	Expiration: 1 * time.Minute,
	Storage:    store,
}))

Разные лимиты для разных маршрутов

strictLimit := limiter.New(limiter.Config{
	Max:        5,
	Expiration: 15 * time.Minute,
})

looseLimit := limiter.New(limiter.Config{
	Max:        1000,
	Expiration: 1 * time.Minute,
})

// Строгий лимит только на /auth/login
app.Post("/auth/login", strictLimit, loginHandler)

// Мягкий лимит на все /api маршруты
app.Use("/api", looseLimit)

Ключ лимита по пользователю (после аутентификации)

app.Use("/api", jwtMiddleware, limiter.New(limiter.Config{
	Max:        500,
	Expiration: 1 * time.Minute,
	KeyGenerator: func(c *fiber.Ctx) string {
		// Берём user_id из JWT-claims, записанных jwtMiddleware
		token := c.Locals("user").(*jwt.Token)
		claims := token.Claims.(jwt.MapClaims)
		if uid, ok := claims["user_id"]; ok {
			return fmt.Sprintf("user:%v", uid)
		}
		return c.IP()
	},
}))

Заголовки ответа

Middleware автоматически добавляет заголовки:

  • X-RateLimit-Limit — максимально допустимое количество запросов.
  • X-RateLimit-Remaining — оставшееся количество запросов в текущем окне.
  • X-RateLimit-Reset — Unix timestamp сброса счётчика.

Чтобы отключить эти заголовки, установите SkipFailedRequests: true или переопределите обработчик.

Подводные камни

  • In-memory не работает горизонтально — при нескольких репликах каждый инстанс хранит свой счётчик; без Redis лимит де-факто умножается на количество экземпляров.
  • IP-спуфинг через X-Forwarded-Forc.IP() за прокси вернёт адрес прокси; используйте app := fiber.New(fiber.Config{TrustedProxies: []string{"10.0.0.0/8"}}) и c.IP() правильно вернёт реальный IP только если прокси доверенный.
  • Алгоритм фиксированного окна — в конце одного окна и начале следующего пользователь может отправить 2 * Max запросов за короткий промежуток; для строгого ограничения используйте sliding window через кастомный Storage.
  • Порядок middleware — rate limiter должен стоять после CORS (иначе preflight заблокируется), но перед тяжёлой бизнес-логикой.
  • Redis недоступен — при сбое Redis-хранилища middleware паникует или пропускает все запросы в зависимости от реализации; добавьте проверку соединения при старте.
  • SkipSuccessfulRequests / SkipFailedRequests — легко перепутать: SkipSuccessfulRequests: true считает только ошибки (полезно против brute-force), SkipFailedRequests: true считает только успешные запросы.
  • Отсутствие Retry-After — стандарт HTTP 429 предполагает заголовок Retry-After; встроенный middleware его не выставляет — добавьте вручную в LimitReached.

Common mistakes

  • Давать ответ про rate limiting в Fiber только на уровне определения, не показывая поведение в реальном приложении.
  • Игнорировать границы ответственности вокруг темы «rate limiting в Fiber»: кто отменяет работу, кто владеет ресурсом и где формируется ответ клиенту.
  • Не связывать rate limiting в Fiber с observability, тестированием или безопасностью, когда это влияет на продакшен-поведение.

What the interviewer is testing

  • Точно объясняет, что именно делает rate limiting в Fiber и где это используется в Go-коде.
  • Связывает rate limiting в Fiber с корректным lifecycle запроса, отменой, конкурентностью или конфигурацией сервера там, где это уместно.
  • Не изобретает API и опирается на реальные контракты официальной документации.

Sources

Related topics