Learn how to secure Node.js APIs from brute-force attacks using rate limiting. Step-by-step guide with code for express-rate-limit, Redis integration, and login protection
APIs are common targets for brute-force login attempts and denial-of-service (DoS) attacks. If left unprotected, attackers can flood your server with requests, guess user credentials, or exhaust system resources. Implementing rate limiting in your Node.js API is one of the best defenses.
This guide will show you how to implement rate limiting, prevent brute-force attacks, and secure your APIs with practical coding examples.
1. What is Rate Limiting?
Rate limiting restricts the number of requests a user (or IP address) can make to your API in a given timeframe.
For example:
- Allow 100 requests per minute per IP
- Block additional requests until the next time window
This helps:
- Prevent brute-force login attempts
- Reduce server overload
- Control abusive usage
2. Install Required Libraries
We’ll use express-rate-limit
and helmet
for security:
npm install express-rate-limit helmet
3. Basic Rate Limiting in Express
const express = require('express');
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const app = express();
// Add security headers
app.use(helmet());
// Apply rate limiting to all requests
const apiLimiter = 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.'
});
app.use('/api/', apiLimiter);
app.get('/api/data', (req, res) => {
res.json({ message: 'Success!' });
});
app.listen(3000, () => console.log('Server running on port 3000'));
Each IP can make 100 requests per 15 minutes. Beyond that, requests are blocked.
4. Protecting Login Endpoints from Brute-Force Attacks
For login routes, attackers often try password spraying or brute-force attempts. You should apply stricter limits:
// Stricter rate limiter for login
const loginLimiter = rateLimit({
windowMs: 5 * 60 * 1000, // 5 minutes
max: 5, // limit each IP to 5 login attempts
message: 'Too many login attempts. Please try again later.'
});
app.post('/api/login', loginLimiter, (req, res) => {
// Your login logic
res.json({ status: 'Login attempt received' });
});
Now, an IP can only make 5 login attempts every 5 minutes.
5. Advanced: Use Redis for Distributed Rate Limiting
If your app runs on multiple servers, you need centralized storage for tracking requests.
Install:
npm install rate-limit-redis ioredis
Usage
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');
const redisClient = new Redis();
const redisRateLimiter = rateLimit({
store: new RedisStore({
sendCommand: (...args) => redisClient.call(...args),
}),
windowMs: 60 * 1000, // 1 minute
max: 50,
message: 'Too many requests, please slow down.'
});
app.use('/api/', redisRateLimiter);
Works across multiple server instances.
6. Use Captcha for Login Protection
After a few failed login attempts, you can enforce CAPTCHA:
- Integrate Google reCAPTCHA or hCaptcha
- Only trigger after failed attempts
- Prevent bots from brute forcing
7. Monitoring & Alerts
-
Use PM2 monitoring (
pm2 monit
) - Track failed login attempts in logs
- Alert admins when unusual traffic spikes occur
8. Best Practices
- Always apply stricter limits on sensitive routes (login, password reset).
- Use Redis/Memcached for distributed systems.
- Implement IP whitelisting for trusted internal APIs.
- Combine rate limiting + CAPTCHA + account lockouts.
- Monitor and log suspicious activity.
Conclusion
By implementing rate limiting in Node.js APIs, you can drastically reduce the risk of brute-force attacks and prevent abusive traffic from overwhelming your system. Start with express-rate-limit
for simple setups and move to Redis-backed solutions for large-scale deployments.
A layered approach (rate limiting, CAPTCHA, and monitoring) ensures your API stays secure and reliable.
0 Comments