Skip to main content

How to use

If you want to start restricting specific routes, you can import the RateLimit class from the plugin and then use it as follows:
src/api/middlewares.ts
import { defineMiddlewares } from "@medusajs/medusa"
import { RateLimit } from '@perseidesjs/medusa-plugin-rate-limit'
import { Modules } from "@medusajs/framework/utils"

export default defineMiddlewares({
  routes: [
    {
      matcher: "/store/custom*",
      middlewares: [
        async (req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction) => {
          const cacheService = req.scope.resolve(Modules.CACHE)
          const rateLimit = new RateLimit({
            cacheService,
            options: {
              limit: 50, // 50 requests per minute
              window: 60
            }
          })

          const ip = req.socket.remoteAddress as string

          // đŸĒ„ This is where the magic happens
          const { success } = await rateLimit.limit(ip)

          if (!success) {
            res.status(429).send('Too many requests, please try again later.')
            return
          }

          next()
        }
      ],
    },
  ],
})
When building custom middleware, use req.socket.remoteAddress for the client IP. Avoid using x-forwarded-for directly as it can be spoofed. If you need proxy support, use the built-in ipRateLimit middleware with trustProxy instead.
The idea is simple: limit the number of requests by the identifier/key of your choice. This is useful if you want to protect based on some business logic aspects, for example by basing it on a license plate in the context of a car website or by user id, you are free to manage this as you wish.
src/api/middlewares.ts
  // â„šī¸  A few examples in action...

  // For example by header
  await rateLimit.limit(req.headers['x-plate'])

  // By user id 
  await rateLimit.limit(req.auth_context.actor_id)

  // Or by a static value that will be global to all users
  await rateLimit.limit('global')

Built-in IP Rate Limiting

For the common use case of IP-based rate limiting, V3 includes a ready-to-use ipRateLimit middleware:
src/api/middlewares.ts
import { defineMiddlewares } from "@medusajs/medusa"
import { ipRateLimit } from '@perseidesjs/medusa-plugin-rate-limit'

export default defineMiddlewares({
  routes: [
    {
      matcher: "/store/auth*",
      middlewares: [ipRateLimit({
        limit: 10,
        window: 60
      })],
    },
  ],
})
The ipRateLimit middleware automatically:
  • Extracts the client IP address
  • Sets appropriate rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining)
  • Returns a 429 status when the limit is exceeded

Extracting the client IP

By default, ipRateLimit uses req.socket.remoteAddress (the direct connection IP). This prevents attackers from bypassing rate limiting by spoofing the X-Forwarded-For header. If your server is behind a reverse proxy (nginx, Cloudflare, etc.), enable trustProxy:
src/api/middlewares.ts
ipRateLimit({
  limit: 10,
  window: 60,
  trustProxy: 1  // Number of trusted proxy hops
})
trustProxy valueBehavior
false (default)Uses direct connection IP. Ignores X-Forwarded-For.
trueUses leftmost IP from X-Forwarded-For. Only use if your proxy overwrites the header.
numberNumber of trusted proxy hops. Extracts IP from the right side of the header, preventing spoofing.
Example with trustProxy: 1:
X-Forwarded-For: spoofed-ip, real-client-ip
                            ↑ uses this (rightmost)
Only enable trustProxy if your server is behind a trusted reverse proxy. Enabling it without a proxy allows clients to spoof their IP and bypass rate limiting.