Skip to main content

Rate Limiting Protection

Overview

Rate limiting restricts the number of requests from a single IP or session within a time window to prevent brute-force attacks, DoS, and API abuse.

Why This Is Important

The Problem: Unlimited Request Abuse

Without rate limiting, attackers can flood your application:

1. Brute-Force Password Attacks

// Attacker tries 1000 passwords per second
for (let i = 0; i < 1000000; i++) {
fetch('/api/auth/login', {
method: 'POST',
body: JSON.stringify({
username: 'admin',
password: commonPasswords[i]
})
});
}

What Happens Without Protection:

  • Unlimited login attempts
  • Password eventually cracked
  • Account compromised

2. Denial of Service (DoS)

// Attacker floods API with requests
while (true) {
fetch('/api/expensive-operation', {
method: 'POST',
body: JSON.stringify({ data: 'x'.repeat(1000000) })
});
}

What Happens Without Protection:

  • Server overwhelmed
  • CPU/memory exhausted
  • Application crashes
  • All users affected

Impact: Service outage Business disruption Revenue loss Customer complaints

3. Data Scraping

// Attacker scrapes all user profiles
for (let id = 1; id <= 1000000; id++) {
const user = await fetch(`/api/user/${id}`);
await saveToDatabase(user);
}

What Happens Without Protection:

  • Entire database scraped
  • User data stolen
  • Competitive intelligence gathered

Impact:

  • Data theft
  • Privacy violations
  • Competitive disadvantage
  • Legal liability

4. API Abuse / Resource Exhaustion

// Attacker generates expensive reports repeatedly
for (let i = 0; i < 10000; i++) {
fetch('/api/reports/generate', {
method: 'POST',
body: JSON.stringify({
dateRange: 'all',
includeAll: true
})
});
}

What Happens Without Protection:

  • Server resources consumed
  • Database overloaded
  • Response times increase
  • Costs skyrocket

Impact:

  • Excessive cloud bills
  • Performance degradation
  • Database overload
  • Financial damage

How nextjs-fortress Solves This

The Algorithm

/**
* Rate limiter - isolated rate limiting logic
*/
export class RateLimiter {
private store = new Map<string, RateLimitEntry>();

async check(request: NextRequest): Promise<RateLimitResult> {
const ip = request.headers.get('x-forwarded-for') || 'unknown';
const now = Date.now();
const rateLimitConfig = this.config.modules.rateLimit;

if (!rateLimitConfig.byIP) {
return { allowed: true };
}

const key = `ip:${ip}`;
const limit = rateLimitConfig.byIP;
const entry = this.store.get(key);

// Create new entry
if (!entry || now > entry.resetAt) {
this.store.set(key, {
count: 1,
resetAt: now + limit.window,
});
return { allowed: true };
}

// Check if limit exceeded
if (entry.count >= limit.requests) {
return {
allowed: false,
response: new NextResponse('Too Many Requests', {
status: 429,
headers: {
'Retry-After': String(Math.ceil((entry.resetAt - now) / 1000)),
},
}),
};
}

// Increment count
entry.count++;
this.store.set(key, entry);

return { allowed: true };
}

cleanup(): void {
const now = Date.now();
for (const [key, entry] of this.store.entries()) {
if (now > entry.resetAt) {
this.store.delete(key);
}
}
}
}

Middleware Integration

// middleware/index.ts
export function createFortressMiddleware(config: FortressConfig) {
const rateLimiter = new RateLimiter(config);

return async function fortressMiddleware(request: NextRequest) {
// Check rate limit
if (config.modules.rateLimit.enabled) {
const rateLimitResult = await rateLimiter.check(request);
if (!rateLimitResult.allowed) {
return rateLimitResult.response; // Returns 429
}
}

// Continue with other validations...
return NextResponse.next();
}
}

Real Implementation Examples

Middleware (Automatic)

// middleware.ts
import { createFortressMiddleware } from 'nextjs-fortress';
import { fortressConfig } from './fortress.config';

export const middleware = createFortressMiddleware(fortressConfig);

// Rate limiting applies to all routes
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

API Route (Custom Limits)

// app/api/login/route.ts
import { withFortress } from 'nextjs-fortress';

export const POST = withFortress(
async (request) => {
const { username, password } = await request.json();

// Authenticate user
const user = await authenticateUser(username, password);

return Response.json({ success: true, user });
},
{
rateLimit: {
requests: 5, // Only 5 login attempts
window: 300000, // per 5 minutes
},
}
);

Server Action (Rate Limited)

// app/actions.ts
'use server';

import { secureServerAction } from 'nextjs-fortress';

export const sendEmail = secureServerAction(
async (to: string, subject: string, body: string) => {
await emailService.send({ to, subject, body });
return { success: true };
},
{
rateLimitKey: 'send-email', // Custom key for this action
}
);

Attack Prevention Examples

Example 1: Brute Force Login

// Attacker tries multiple passwords
POST /api/auth/login (attempt 1)
POST /api/auth/login (attempt 2)
POST /api/auth/login (attempt 3)
POST /api/auth/login (attempt 4)
POST /api/auth/login (attempt 5)
POST /api/auth/login (attempt 6)

// Fortress response:
Status: 429 Too Many Requests
Retry-After: 300 (seconds)

// Result: Attack stopped after 5 attempts

Example 2: DoS Attack

// Attacker floods server
POST /api/data (1-100)Allowed
POST /api/data (101)Rate limited

// Response:
{
"error": "Too Many Requests",
"retryAfter": 45
}

// All subsequent requests from IP blocked for 45 seconds

Example 3: Data Scraping

// Scraper tries to get all users
GET /api/user/1
GET /api/user/2
// ... 98 more requests
GET /api/user/100
GET /api/user/101

// Rate limit exceeded
// Scraping prevented

Rate Limit Headers

// Fortress adds these headers to responses

X-RateLimit-Limit: 100 // Total allowed
X-RateLimit-Remaining: 45 // Remaining in window
X-RateLimit-Reset: 1641234567 // Unix timestamp

// When rate limited:
Retry-After: 45 // Seconds until retry

How to Initialize

import { FortressConfig, FortressLogger } from '@mindfiredigital/nextjs-fortress';

export const fortressConfig: FortressConfig = {
enabled: true,
mode: 'development',

modules: {
rateLimit: {
enabled: true,

// Global IP-based limit
byIP: {
requests: 100,
window: 60000,
},

// Session-based limit
bySession: {
requests: 50,
window: 60000,
},

// Endpoint-specific limits
endpoints: {
'/api/auth/login': {
requests: 5,
window: 300000,
},
},

// Whitelisted IPs (no limits)
whitelist: [
'127.0.0.1',
'::1',
],
},
},

onSecurityEvent: async (event) => {
if (event.type === 'ratelimit') {
const logger = new FortressLogger({
enabled: true,
level: 'warn',
destination: 'console',
});

logger.warn('🚦 Rate Limit Exceeded', {
ip: event.request.ip,
path: event.request.path,
timestamp: event.timestamp,
});
}
},
};

Common Configurations

Strict (High Security)

byIP: {
requests: 50,
window: 60000,
}

Moderate (Balanced)

byIP: {
requests: 100,
window: 60000,
}

Relaxed (User-Friendly)

byIP: {
requests: 200,
window: 60000,
}

Summary

What happens without rate limiting:

  • Brute-force attacks succeed
  • DoS attacks crash server
  • Data scraping steals information
  • API abuse costs money

What nextjs-fortress provides:

  • IP-based limiting
  • Session-based limiting
  • Endpoint-specific limits
  • Automatic cleanup

How to initialize:

rateLimit: {
enabled: true,
byIP: {
requests: 100, // Requests allowed
window: 60000, // Time window (ms)
},
}

Related Documentation: