Express Rate Limiting Middleware
express-rate-limit is a middleware for Express.js that provides basic IP-based rate limiting to protect endpoints from abuse, such as brute-force attacks on login or password reset forms, or excessive API requests. The current stable version is 8.3.2, and the package maintains an active release cadence, with multiple minor and patch updates within recent months, indicating ongoing development and support. Key differentiators include its flexible configuration for `windowMs` and `limit`, support for various external data stores (beyond its built-in memory store), and compliance with the IETF RateLimit header specification (draft-6, draft-7, and draft-8), allowing for modern and standardized rate limiting headers. It also includes `ipv6Subnet` configuration for granular IPv6 handling and integrates well with related packages like `express-slow-down`.
Common errors
-
TypeError: rateLimit is not a function
cause Attempting to use `new rateLimit()` or incorrectly importing the function, especially in CommonJS where the default export might be expected directly.fixEnsure you are using named import: `import { rateLimit } from 'express-rate-limit'` for ESM, or `const { rateLimit } = require('express-rate-limit')` for CommonJS. Do not use `new` with `rateLimit`. -
Error: Store must be defined (if using a custom store with 'store' option)
cause The `store` option was specified but its value was `undefined` or `null`, often due to an uninitialized external store instance.fixEnsure that the `store` option is provided with an instantiated store object, e.g., `store: new RedisStore({ client: redisClient })`. -
Rate limiting not being applied or not working as expected (e.g., still receiving 200 OK after many requests)
cause The middleware might be applied globally when it should be route-specific, or vice-versa, or applied in the wrong order in your Express middleware chain.fixVerify that `app.use(limiter)` is called *before* the routes you intend to limit. If limiting specific routes, apply `app.get('/route', limiter, handler)` instead of globally. Check your `windowMs` and `limit` values are not excessively high.
Warnings
- breaking Version 8.0.0 introduced breaking changes. Specifically, the library shifted towards adhering more closely to IETF RateLimit header specifications. Review your configuration, especially `standardHeaders` and `legacyHeaders`.
- gotcha By default, `express-rate-limit` uses a `MemoryStore`. In production environments with multiple Node.js instances or processes, this can lead to inconsistent rate limiting as each instance will have its own independent hit count.
- gotcha The `ipv6Subnet` option is crucial for correctly identifying unique users on IPv6 networks. An incorrect setting can either be too aggressive (treating many users in a subnet as one) or too lenient (allowing more requests than intended).
- deprecated The traditional `X-RateLimit-*` headers are considered legacy. While still supported via the `legacyHeaders: true` option, the library encourages adoption of the IETF RateLimit header specification.
Install
-
npm install express-rate-limit -
yarn add express-rate-limit -
pnpm add express-rate-limit
Imports
- rateLimit
const rateLimit = require('express-rate-limit')import { rateLimit } from 'express-rate-limit' - RateLimitRequestHandler
import { type RateLimitRequestHandler } from 'express-rate-limit' - MemoryStore
import { Store } from 'express-rate-limit'import { MemoryStore } from 'express-rate-limit'
Quickstart
import express from 'express';
import { rateLimit } from 'express-rate-limit';
const app = express();
// Configure the rate limiter: 100 requests per IP every 15 minutes.
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100, // Max 100 requests per IP per window
standardHeaders: 'draft-8', // Uses the latest IETF RateLimit header draft
legacyHeaders: false, // Disables the older X-RateLimit-* headers
message: 'Too many requests from this IP, please try again after 15 minutes.',
// store: new RedisStore({ /* ... config ... */ }), // Example of an external store
});
// Apply the rate limiting middleware to all API requests
app.use('/api/', apiLimiter);
// A public route that is not rate-limited
app.get('/', (req, res) => {
res.send('Welcome! This route is not rate-limited.');
});
// A rate-limited API endpoint
app.get('/api/data', (req, res) => {
res.json({ message: 'This is some data.' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});