In today’s world of public APIs and high-traffic web services, protecting your application from abuse and overload is essential. Rate limiting is one of the most effective strategies to control how often clients can hit your endpoints.


What is a rate limiter?

A rate limiter restricts the number of requests a user or client can make to a server within a specific time window. For example, you might allow a user to make 100 requests per 15 minutes. Once they reach that limit, additional requests will be blocked or delayed.


Why is rate limiting important?

  1. Security – Prevent brute-force attacks and abusive behaviors.
  2. Stability – Avoid server overload due to excessive traffic.
  3. Fairness – Ensure equal usage for all users.
  4. Cost Control – Limit access to expensive operations or external APIs.

Implementing a rate limiter in express

The easiest way to implement rate limiting in Express is using the express-rate-limit middleware.

1. Install the package

npm install express-rate-limit

2. Basic usage example

const express = require('express');
const rateLimit = require('express-rate-limit');

const app = express();

// Create a limiter: 100 requests per 15 minutes per IP
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each IP to 100 requests per windowMs
  message: 'Too many requests from this IP, please try again later.',
});

// Apply the limiter to all requests
app.use(limiter);

app.get('/', (req, res) => {
  res.send('Hello, world!');
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

This configuration limits every IP address to 100 requests per 15-minute window. If a user exceeds that limit, they will receive a 429 Too Many Requests response.


Applying the limiter to specific routes

You don’t have to apply rate limiting globally. You can also restrict it to specific routes:

const authLimiter = rateLimit({
  windowMs: 5 * 60 * 1000, // 5 minutes
  max: 5, // Limit to 5 requests per window
  message: 'Too many login attempts. Try again in 5 minutes.',
});

app.post('/login', authLimiter, (req, res) => {
  // login logic
});

This is especially useful for sensitive routes like /login or /signup.


Advanced options

You can customize rate limiting even further with options like:

  • keyGenerator: Define how clients are identified (e.g., by IP, user ID, or token).
  • skip: Conditionally skip certain requests (e.g., internal users).
  • handler: Customize the error response when the limit is hit.

Example:

const limiter = rateLimit({
  windowMs: 10 * 60 * 1000,
  max: 50,
  keyGenerator: (req) => req.headers['x-api-key'] || req.ip,
  skip: (req) => req.user?.isAdmin,
  handler: (req, res) => {
    res.status(429).json({ error: 'Rate limit exceeded. Slow down!' });
  },
});

Tips for using rate limiting

  • Combine with authentication to create per-user limits instead of per-IP.
  • Use Redis or an external store in production for distributed environments.
  • Log when limits are hit to detect abuse patterns.
  • Be mindful of mobile networks and proxies that might share IP addresses.

Conclusion

Rate limiting is a crucial part of building secure and resilient APIs. With tools like express-rate-limit, implementing it in Express is simple and highly configurable. Whether you want to protect login routes, reduce API abuse, or improve server stability, rate limiting should be part of your backend toolkit.