Skip to content
Back to skills

Rate Limit Abuse Prevention

ASecurity

API rate limiting, bot protection, DDoS mitigation, and abuse defense / Rate limiting API, perlindungan bot, dan mitigasi serangan DDoS.

  • 125 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 12, 2026
ai-agentstypescriptrustgoreactnextjsnoderailsapidatabasebackend

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned November 12, 2026

npx -y skills add roedyrustam/vibes-plug --skill rate-limit-abuse-prevention --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rate Limit Abuse Prevention?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rate Limit Abuse Prevention
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/roedyrustam-rate-limit-abuse-prevention-vibes-plug/badge)](https://www.skillsdirectory.com/skills/roedyrustam-rate-limit-abuse-prevention-vibes-plug)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: rate-limit-abuse-prevention
description: "API rate limiting, bot protection, DDoS mitigation, and abuse defense / Rate limiting API, perlindungan bot, dan mitigasi serangan DDoS."
author: "Roedy Rustam"
version: "4.8.0"

---

# Rate Limit & Abuse Prevention (2026 Edition)

[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)

---

<a name="english"></a>
## English

### Orchestration & Integration
- **Threat Defense & Red Teaming**: `autonomous-red-teamer`, `prompt-injection-firewall`, `zero-trust-secret-vault`
- **Identity & Authentication**: `authentication-identity-expert`, `compliance-gdpr-privacy-expert`
- **API Architecture & Backend**: `api-design-expert`, `js-backend-expert`, `api-gateway-proxy-expert`
- **Edge Infrastructure & DDoS**: `cloud-hosting-expert`, `cloudflare-workers-expert`
- **SaaS Billing & Quotas**: `saas-billing`, `doku-payment-gateway`, `payment-gateway-expert`
- **Lifecycle & Hardening**: `production-ready-hardener`, `brainstorming`, `zero-to-prod-orchestrator`, `session-memory-manager`

### Description
Production-grade guide for protecting APIs and web applications from abuse, overuse, and attacks. Covers **rate limiting algorithms** (Token Bucket, Sliding Window), **Upstash Ratelimit**, **bot protection** (Cloudflare Turnstile, hCaptcha), **DDoS mitigation** at edge, **brute-force login prevention**, **API key management & usage quotas**, and **response headers** (X-RateLimit, Retry-After).

### Trigger Conditions
Activate this skill when:
- Implementing API rate limiting for public or authenticated endpoints.
- Adding bot protection (CAPTCHA) to forms (login, signup, contact).
- Setting up DDoS protection at the edge (Cloudflare, Vercel).
- Preventing brute-force attacks on authentication endpoints.
- Implementing API key issuance and usage quotas for SaaS.
- Building tiered rate limits based on subscription plans.

---

### Rate Limiting Algorithm Comparison

| Algorithm | Behavior | Best For | Burst Handling |
|---|---|---|---|
| **Fixed Window** | Resets counter at interval boundary | Simple endpoints | Allows double burst at window edge |
| **Sliding Window** | Rolling window, smooth distribution | API endpoints | Smooth, no edge burst |
| **Token Bucket** | Tokens refill at fixed rate | High-throughput APIs | Allows controlled bursts |
| **Leaky Bucket** | Processes at fixed rate, queues excess | Stream processing | No bursts, constant rate |

**Recommendation**: Use **Sliding Window** for most API endpoints. Use **Token Bucket** for endpoints that should allow burst traffic.

---

### 1. Upstash Ratelimit (Edge-Compatible)

```typescript
// lib/ratelimit.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const redis = Redis.fromEnv();

// Different rate limiters for different tiers
export const rateLimiters = {
  /** Public API: 10 requests per 10 seconds */
  public: new Ratelimit({
    redis,
    limiter: Ratelimit.slidingWindow(10, '10s'),
    prefix: 'rl:public',
    analytics: true,
  }),

  /** Authenticated API: 100 requests per minute */
  authenticated: new Ratelimit({
    redis,
    limiter: Ratelimit.slidingWindow(100, '1m'),
    prefix: 'rl:auth',
    analytics: true,
  }),

  /** Pro tier: 1000 requests per minute */
  pro: new Ratelimit({
    redis,
    limiter: Ratelimit.slidingWindow(1000, '1m'),
    prefix: 'rl:pro',
    analytics: true,
  }),

  /** Login endpoint: 5 attempts per 15 minutes */
  login: new Ratelimit({
    redis,
    limiter: Ratelimit.slidingWindow(5, '15m'),
    prefix: 'rl:login',
  }),

  /** Password reset: 3 per hour */
  passwordReset: new Ratelimit({
    redis,
    limiter: Ratelimit.slidingWindow(3, '1h'),
    prefix: 'rl:pwreset',
  }),
};
```

```typescript
// middleware.ts (Next.js)
import { NextResponse, type NextRequest } from 'next/server';
import { rateLimiters } from '@/lib/ratelimit';

export async function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/api/')) {
    const ip = request.headers.get('x-forwarded-for') ?? request.ip ?? '127.0.0.1';
    const identifier = ip;

    const limiter = request.nextUrl.pathname.startsWith('/api/auth/login')
      ? rateLimiters.login
      : rateLimiters.public;

    const { success, limit, remaining, reset } = await limiter.limit(identifier);

    if (!success) {
      return NextResponse.json(
        { error: 'Too many requests', retryAfter: Math.ceil((reset - Date.now()) / 1000) },
        {
          status: 429,
          headers: {
            'X-RateLimit-Limit': limit.toString(),
            'X-RateLimit-Remaining': '0',
            'X-RateLimit-Reset': reset.toString(),
            'Retry-After': Math.ceil((reset - Date.now()) / 1000).toString(),
          },
        },
      );
    }

    const response = NextResponse.next();
    response.headers.set('X-RateLimit-Limit', limit.toString());
    response.headers.set('X-RateLimit-Remaining', remaining.toString());
    response.headers.set('X-RateLimit-Reset', reset.toString());
    return response;
  }
}

export const config = { matcher: '/api/:path*' };
```

---

### 2. Tiered Rate Limits for SaaS

```typescript
// lib/tiered-ratelimit.ts
import { Ratelimit } from '@upstash/ratelimit';

type PlanTier = 'free' | 'pro' | 'enterprise';

const PLAN_LIMITS: Record<PlanTier, { requests: number; window: string }> = {
  free:       { requests: 100,    window: '1h' },
  pro:        { requests: 5000,   window: '1h' },
  enterprise: { requests: 50000,  window: '1h' },
};

export function getRateLimiterForPlan(plan: PlanTier): Ratelimit {
  const config = PLAN_LIMITS[plan];
  return new Ratelimit({
    redis,
    limiter: Ratelimit.slidingWindow(config.requests, config.window as any),
    prefix: `rl:api:${plan}`,
  });
}

// Usage in API route
export async function GET(request: Request) {
  const user = await getAuthUser(request);
  const limiter = getRateLimiterForPlan(user.plan);
  const { success, remaining } = await limiter.limit(user.id);

  if (!success) {
    return Response.json({
      error: 'Rate limit exceeded',
      upgrade: user.plan !== 'enterprise' ? 'https://app.example.com/pricing' : undefined,
    }, { status: 429 });
  }

  return Response.json({ success: true, message: 'Request processed within rate limit allocation.' });
}
```

---

### 3. Bot Protection (Cloudflare Turnstile)

```tsx
// components/turnstile.tsx
'use client';

import { Turnstile } from '@marsidev/react-turnstile';

interface TurnstileWidgetProps {
  onVerify: (token: string) => void;
}

export function TurnstileWidget({ onVerify }: TurnstileWidgetProps) {
  return (
    <Turnstile
      siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY!}
      onSuccess={onVerify}
      options={{ theme: 'auto', size: 'flexible' }}
    />
  );
}
```

```typescript
// Server-side verification
export async function verifyTurnstileToken(token: string, ip?: string): Promise<boolean> {
  const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      secret: process.env.TURNSTILE_SECRET_KEY,
      response: token,
      remoteip: ip,
    }),
  });

  const data = await response.json();
  return data.success === true;
}
```

---

### 4. Brute-Force Login Prevention

```typescript
// lib/login-protection.ts
const MAX_ATTEMPTS = 5;
const LOCKOUT_DURATION = 15 * 60; // 15 minutes in seconds
const PROGRESSIVE_DELAYS = [0, 1000, 2000, 4000, 8000]; // Progressive delay per attempt

export async function checkLoginAttempt(email: string, ip: string): Promise<{
  allowed: boolean;
  remainingAttempts: number;
  lockoutEndsAt?: Date;
}> {
  const key = `login-attempts:${email}:${ip}`;
  const attempts = await redis.incr(key);

  if (attempts === 1) {
    await redis.expire(key, LOCKOUT_DURATION);
  }

  if (attempts > MAX_ATTEMPTS) {
    const ttl = await redis.ttl(key);
    return {
      allowed: false,
      remainingAttempts: 0,
      lockoutEndsAt: new Date(Date.now() + ttl * 1000),
    };
  }

  // Progressive delay
  const delay = PROGRESSIVE_DELAYS[Math.min(attempts - 1, PROGRESSIVE_DELAYS.length - 1)];
  if (delay > 0) await new Promise(r => setTimeout(r, delay));

  return { allowed: true, remainingAttempts: MAX_ATTEMPTS - attempts };
}

export async function clearLoginAttempts(email: string, ip: string): Promise<void> {
  await redis.del(`login-attempts:${email}:${ip}`);
}
```

---

### 5. API Key Management

```typescript
// lib/api-keys.ts
import { nanoid } from 'nanoid';
import { hash, verify } from '@node-rs/argon2';

export async function createApiKey(userId: string, name: string) {
  const rawKey = `sk_live_${nanoid(32)}`;
  const prefix = rawKey.slice(0, 12); // Show prefix for identification
  const hashedKey = await hash(rawKey);

  await db.insert(apiKeys).values({
    userId,
    name,
    prefix,
    hashedKey,
    lastUsedAt: null,
  });

  // Return raw key only once — cannot be retrieved later
  return { key: rawKey, prefix };
}

export async function validateApiKey(rawKey: string) {
  const prefix = rawKey.slice(0, 12);
  const candidates = await db.query.apiKeys.findMany({
    where: and(
      eq(apiKeys.prefix, prefix),
      eq(apiKeys.isRevoked, false),
    ),
  });

  for (const candidate of candidates) {
    if (await verify(candidate.hashedKey, rawKey)) {
      // Update last used timestamp
      await db.update(apiKeys)
        .set({ lastUsedAt: new Date() })
        .where(eq(apiKeys.id, candidate.id));
      return candidate;
    }
  }

  return null;
}
```

---

### 6. Atomic Redis Lua Token-Bucket & Cost-Weighted LLM Quota Limiter (TypeScript)
For AI/LLM endpoints or multi-query GraphQL/batch operations where a single request consumes variable compute units (`costTokens`), enforce an atomic Redis Lua token bucket so check-and-decrement never suffers from race conditions:

```typescript
// lib/atomic-cost-bucket.ts
const TOKEN_BUCKET_LUA = `
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local refillRatePerSec = tonumber(ARGV[2])
local requestedCost = tonumber(ARGV[3])
local nowMs = tonumber(ARGV[4])

local state = redis.call('HMGET', key, 'tokens', 'lastRefillMs')
local tokens = tonumber(state[1])
local lastRefillMs = tonumber(state[2])

if tokens == nil then
  tokens = capacity
  lastRefillMs = nowMs
else
  local elapsedSec = math.max(0, (nowMs - lastRefillMs) / 1000.0)
  tokens = math.min(capacity, tokens + (elapsedSec * refillRatePerSec))
  lastRefillMs = nowMs
end

if tokens < requestedCost then
  local deficit = requestedCost - tokens
  local retryAfterSec = math.ceil(deficit / refillRatePerSec)
  return { 0, math.floor(tokens), retryAfterSec }
end

tokens = tokens - requestedCost
local ttlSec = math.max(60, math.ceil(capacity / refillRatePerSec) * 2)
redis.call('HMSET', key, 'tokens', tokens, 'lastRefillMs', lastRefillMs)
redis.call('EXPIRE', key, ttlSec)
return { 1, math.floor(tokens), 0 }
`;

export interface RedisEvalClient {
  eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
}

export async function consumeWeightedRateLimit(
  redisClient: RedisEvalClient,
  bucketKey: string,
  capacity: number,
  refillRatePerSec: number,
  costUnits = 1
): Promise<{ allowed: boolean; remainingTokens: number; retryAfterSeconds: number }> {
  const raw = (await redisClient.eval(
    TOKEN_BUCKET_LUA,
    1,
    bucketKey,
    capacity,
    refillRatePerSec,
    costUnits,
    Date.now()
  )) as [number, number, number];

  return {
    allowed: raw[0] === 1,
    remainingTokens: raw[1],
    retryAfterSeconds: raw[2],
  };
}
```

### Hard Guardrails (Declarative Invariants)
- **Never Use In-Memory `Map` for Multi-Instance Rate Limiting**: Process-local `new Map()` counters reset on deploy and fail across horizontal replicas; always back rate limiters with atomic Redis/Upstash Lua scripts or Cloudflare Durable Objects.
- **Composite Identity Keys Over Raw IP Alone**: Rate limit authenticated endpoints by `tenantId:userId` or `apiKeyPrefix` rather than IP alone so corporate NATs and mobile carrier CGNATs are not falsely throttled.
- **Mandatory `Retry-After` & `X-RateLimit-*` Headers on HTTP 429**: Every `429 Too Many Requests` response must include `Retry-After` (seconds) and `X-RateLimit-Reset` so compliant SDKs back off automatically.
- **Hash API Keys with Argon2id / SHA-256 at Rest**: Never store plaintext API keys in the database; store only a short display prefix (`sk_live_ab12...`) and a cryptographic hash verified in constant time.

### Response Headers Specification

| Header | Purpose | Example |
|---|---|---|
| `X-RateLimit-Limit` | Max requests allowed | `100` |
| `X-RateLimit-Remaining` | Remaining requests in window | `87` |
| `X-RateLimit-Reset` | Unix timestamp when window resets | `1723382400` |
| `Retry-After` | Seconds until retry is allowed (on 429) | `60` |

---

### Common Pitfalls to Avoid

| Anti-Pattern | Problem | Correct Approach |
|---|---|---|
| Rate limiting by IP only | Shared IPs affect multiple users | Combine IP + user ID + API key |
| No rate limit on auth endpoints | Brute-force attacks | Strict limits: 5 attempts / 15 min |
| Fixed window rate limiting | 2x burst at window boundary | Use sliding window algorithm |
| No 429 response headers | Clients can't implement backoff | Always include Retry-After header |
| CAPTCHA on every request | Terrible UX, accessibility issues | Only trigger after suspicious behavior |
| Rate limit in application only | Still hits your server | Add edge-level protection (Cloudflare WAF) |

### Sovereign Execution, Token Efficiency & Invariants
| Dimension | Sovereign Mandate & Deterministic Contract |
| :--- | :--- |
| **Frontier Model Cascade** | High Fast / High (`Grok 4.7 High Fast`, `Claude Sonnet 5.5 High`, `Gemini 4 Pro`) |
| **Availability Fallback** | `Gemini 4 Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6`; `Grok 4.7` -> `Grok 4.6 High Fast` (Zero unhandled missing-model errors) |
| **KV-Cache & Token Protocol** | Pin static instructions & schemas at prompt prefix (`kv-cache-prefix-optimizer`); pass surgical `CONTEXT_MAP.md` slices (<250 tokens/call) during Swarm Fan-Out |
| **SaaT CLI & MCP Dispatch** | CLI: `vibes skill exec rate-limit-abuse-prevention <audit\|eval\|verify\|generate\|blueprint>` / MCP: `vibes_skill_dispatch`, `vibes_critic_validate`, `vibes_self_heal`, `vibes_eval_sandbox`, `vibes_verify_invariants` |
| **NFR & Anti-Slop Invariants** | Zero OWASP Top 10 vectors, 0 hardcoded secrets, >= 90% branch coverage on domain logic, 0 Anti-Slop violations; 100% complete zero-placeholder output verified via `node scripts/check-anti-slop.js --strict` |

---


---

<a name="bahasa-indonesia"></a>
## Bahasa Indonesia

### Integrasi Orkestrasi
- **Pertahanan Ancaman & Red Teaming**: `autonomous-red-teamer`, `prompt-injection-firewall`, `zero-trust-secret-vault`
- **Identitas & Autentikasi**: `authentication-identity-expert`, `compliance-gdpr-privacy-expert`
- **Arsitektur API & Backend**: `api-design-expert`, `js-backend-expert`, `api-gateway-proxy-expert`
- **Infrastruktur Edge & DDoS**: `cloud-hosting-expert`, `cloudflare-workers-expert`
- **SaaS Billing & Kuota**: `saas-billing`, `doku-payment-gateway`, `payment-gateway-expert`
- **Siklus Hidup & Pengerasan**: `production-ready-hardener`, `brainstorming`, `zero-to-prod-orchestrator`, `session-memory-manager`

### Deskripsi
Panduan tingkat produksi untuk melindungi API dan aplikasi web dari penyalahgunaan, beban berlebih, dan serangan cyber. Mencakup **algoritma rate limiting** (Token Bucket, Sliding Window), **Upstash Ratelimit** (kompatibel edge), **perlindungan bot** (Cloudflare Turnstile), **mitigasi DDoS**, **pencegahan brute-force login**, **manajemen API key & kuota penggunaan**, serta format header respons standar (X-RateLimit, Retry-After).

### Kondisi Pemicu
Aktifkan skill ini ketika:
- Mengimplementasikan pembatasan laju (*Rate Limiting*) pada API publik atau terotentikasi.
- Menambahkan verifikasi bot tanpa CAPTCHA yang menyusahkan (Cloudflare Turnstile) pada formulir.
- Mencegah serangan brute-force credential stuffing pada endpoint otentikasi.
- Menetapkan kuota pemanggilan API bertingkat berdasarkan paket langganan SaaS pengguna.

---

### Implementasi Pola Kunci (TypeScript)

#### 1. Rate Limiting di Edge dengan Upstash Redis & Next.js Middleware
```typescript
// lib/pembatas-laju.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const redis = Redis.fromEnv();

export const pembatasLaju = {
  publik: new Ratelimit({
    redis,
    limiter: Ratelimit.slidingWindow(20, '10s'),
    prefix: 'rl:publik',
  }),
  login: new Ratelimit({
    redis,
    limiter: Ratelimit.slidingWindow(5, '15m'), // Maks 5 percobaan per 15 menit
    prefix: 'rl:login',
  }),
};
```

```typescript
// middleware.ts
import { NextResponse, type NextRequest } from 'next/server';
import { pembatasLaju } from '@/lib/pembatas-laju';

export async function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/api/')) {
    const ip = request.headers.get('x-forwarded-for') ?? '127.0.0.1';
    const isLogin = request.nextUrl.pathname.startsWith('/api/auth/login');
    const limiter = isLogin ? pembatasLaju.login : pembatasLaju.publik;

    const { success, limit, remaining, reset } = await limiter.limit(ip);

    if (!success) {
      return NextResponse.json(
        { error: 'Terlalu banyak permintaan. Silakan coba lagi nanti.' },
        {
          status: 429,
          headers: {
            'X-RateLimit-Limit': limit.toString(),
            'X-RateLimit-Remaining': '0',
            'X-RateLimit-Reset': reset.toString(),
            'Retry-After': Math.ceil((reset - Date.now()) / 1000).toString(),
          },
        }
      );
    }

    const response = NextResponse.next();
    response.headers.set('X-RateLimit-Limit', limit.toString());
    response.headers.set('X-RateLimit-Remaining', remaining.toString());
    return response;
  }
}
```

#### 2. Verifikasi Token Bot Cloudflare Turnstile di Sisi Server
```typescript
export async function verifikasiTurnstile(token: string, ipPengguna: string): Promise<boolean> {
  const formData = new FormData();
  formData.append('secret', process.env.TURNSTILE_SECRET_KEY!);
  formData.append('response', token);
  formData.append('remoteip', ipPengguna);

  const res = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
    method: 'POST',
    body: formData,
  });

  const hasil = await res.json();
  return hasil.success === true;
}
```

### Matriks Eksekusi Berdaulat, Efisiensi Token & Invarian
| Dimensi | Mandat Berdaulat & Kontrak Deterministik |
| :--- | :--- |
| **Kaskade Model Frontier** | High Fast / High (`Grok 4.7 High Fast`, `Claude Sonnet 5.5 High`, `Gemini 4 Pro`) |
| **Invarian Fallback Ketersediaan** | `Gemini 4 Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6`; `Grok 4.7` -> `Grok 4.6 High Fast` (Bebas gagal saat model belum tersedia/kuota penuh) |
| **Protokol Hemat Token & KV-Cache** | Kunci instruksi & skema statis di prefix awal (`kv-cache-prefix-optimizer`); kirim potongan `CONTEXT_MAP.md` (<250 token/panggilan) saat Fan-Out |
| **Eksekusi SaaT CLI & MCP** | CLI: `vibes skill exec rate-limit-abuse-prevention <audit\|eval\|verify\|generate\|blueprint>` / MCP: `vibes_skill_dispatch`, `vibes_critic_validate`, `vibes_self_heal`, `vibes_eval_sandbox`, `vibes_verify_invariants` |
| **Invarian NFR & Anti-Slop** | Nol celah OWASP Top 10, 0 kredensial hardcoded, cakupan pengujian >= 90% pada logika domain, 0 pelanggaran Anti-Slop; kode 100% tuntas tanpa placeholder diverifikasi melalui `node scripts/check-anti-slop.js --strict` |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in ai-agents →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.