How to Implement Rate Limiting in Node.js
Rate limiting is a crucial security measure that controls the rate of traffic sent by a client to an API, protecting your application from abuse, brute-force attacks, and Denial of Service (DoS) attempts. This article provides a straightforward guide on how to implement rate limiting in a Node.js and Express application, covering basic memory-based limiting and scaling to production-ready Redis setups.
Why Implement Rate Limiting?
Without rate limiting, malicious users or malfunctioning scripts can flood your API with thousands of requests per second. This can degrade performance for legitimate users, increase hosting costs, or crash your database. Implementing a rate limiter ensures that each user or IP address is restricted to a reasonable number of requests within a specific timeframe.
1. Basic Rate
Limiting with express-rate-limit
For most Node.js applications built with Express, the easiest way to
implement rate limiting is by using the express-rate-limit
package.
First, install the package:
npm install express-rate-limitNext, integrate the middleware into your Express application:
const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();
// Define the rate limiting rule
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
message: 'Too many requests from this IP, please try again after 15 minutes',
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
});
// Apply the rate limiting middleware to all requests
app.use('/api/', apiLimiter);
app.get('/api/data', (req, res) => {
res.send('This is secure API data.');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});2. Key Configuration Options
The express-rate-limit library offers several options to
customize your rate-limiting strategy:
windowMs: The duration of the time window in milliseconds.max: The maximum number of connections allowed during thewindowMswindow.message: The response body sent when a client exceeds the limit.statusCode: The HTTP status code returned (defaults to429 Too Many Requests).skip: A function used to bypass the rate limiter for specific requests (e.g., admin IPs).
3. Scaling to Production with Redis
By default, express-rate-limit stores request counts in
memory. If your Node.js application is load-balanced across multiple
servers or containerized in Docker, the in-memory store will not work
effectively because each server instance will track requests
independently.
To solve this, use an external data store like Redis to track request counts globally.
First, install the Redis store adapter and the Redis client:
npm install rate-limit-redis redisThen, configure the rate limiter to use the Redis backend:
const express = require('express');
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis').default;
const { createClient } = require('redis');
const app = express();
// Create a Redis client
const redisClient = createClient({ url: 'redis://localhost:6379' });
redisClient.connect().catch(console.error);
const redisLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
// Use Redis to store the request state
store: new RedisStore({
sendCommand: (...args) => redisClient.sendCommand(args),
}),
message: 'Too many requests, please try again later.',
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api/', redisLimiter);Using this Redis configuration ensures that no matter how many API instances you run, the rate limits are applied consistently across your entire infrastructure.