Skip to content

ExpressJS - Rate Limiting

Rate limiting is a crucial security and performance technique for web applications. It restricts the number of requests a user (or IP address) can make in a given time frame. This helps prevent abuse, such as:

  • Denial-of-Service (DoS) Attacks: Overwhelming a server with too many requests, degrading performance for legitimate users.
  • Brute-Force Attacks: Repeatedly trying to guess credentials on login or password reset endpoints.
  • Web Scraping: Preventing bots from aggressively scraping content from your site.

The express-rate-limit middleware is the industry standard for implementing rate limiting in Express. It’s highly configurable and easy to use. Key options include:

  • windowMs: The time frame for which requests are checked, in milliseconds. For example, 15 * 60 * 1000 sets a 15-minute window.
  • max (or limit): The maximum number of connections to allow during the windowMs from a single IP. Once the limit is exceeded, subsequent requests will be rejected with a 429 Too Many Requests status code.
  • message: The error message to send when the limit is exceeded. Can be a string, JSON object, or function.
  • standardHeaders: A boolean (true) to enable standardized RateLimit-* response headers.
  • legacyHeaders: A boolean (false) to disable the older, non-standard X-RateLimit-* headers.

Use npm or your preferred package manager to install the dependency.

npm install express-rate-limit

The following code sets up a global rate limiter that allows 100 requests per IP every 15 minutes. It also shows how to create a stricter limiter specifically for an authentication route.

import express from 'express';
import rateLimit from 'express-rate-limit';
const app = express();
const PORT = 3000;
// Create a general-purpose rate limiter
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
standardHeaders: 'draft-7', // Use standard `RateLimit` header syntax
legacyHeaders: false, // Don't send `X-RateLimit-*` headers
message: { error: 'Too many requests, please try again after 15 minutes.' },
});
// Create a stricter rate limiter for sensitive endpoints like login
const loginLimiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 minutes
limit: 5, // Limit each IP to 5 login attempts per `window`
message: { error: 'Too many login attempts, please try again after 10 minutes.' },
standardHeaders: 'draft-7',
legacyHeaders: false,
});
// Apply the general limiter to all requests to `/api`
app.use('/api', apiLimiter);
// A regular API route
app.get('/api/data', (req, res) => {
res.send('This is public data.');
});
// Apply the stricter limiter only to the login route
app.post('/api/login', loginLimiter, (req, res) => {
// ... login logic here ...
res.send('Login attempt recorded.');
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
  1. Run the server (e.g., with npm run dev).
  2. Make more than 5 POST requests to http://localhost:3000/api/login within 10 minutes. On the 6th request, you will receive a 429 Too Many Requests response with your custom JSON error message.
  3. The response headers will include RateLimit-Limit (the max requests), RateLimit-Remaining (requests left), and RateLimit-Reset (a timestamp for when the limit resets).