Implementing Rate Limiting and DDoS Defense on Edge Networks for Public Web APIs

Learn how to protect public web APIs from DDoS spikes and scraper abuse using edge workers, Upstash Redis sliding windows, and Next.js.

Published by Aafaque Nazir on 2026-11-08 • 7 min read
Quick Answer: Edge rate limiting intercepts HTTP requests at global CDN nodes before they reach your origin database or compute server. By pairing edge workers with a distributed store like Upstash Redis using a sliding window algorithm, you block abusive traffic and volumetric DDoS spikes while keeping latency minimal.

The Vulnerability of Origin-Bound API Defense

Most developers implement rate limiting using an in-memory store like Express rate-limit or an internal Redis container running alongside their main backend. While this works for single-server hobby projects, it fails when traffic surges or when targeted by scrapers.

If a botnet floods your Next.js API routes with thousands of requests per second, each request still spins up a serverless function, consumes CPU time, and checks your database. By the time your backend responds with HTTP 429 Too Many Requests, your infrastructure bill has already climbed and legitimate users face high latency.

I encountered this exact problem while building [Aura Estate](/projects/15), a real estate portal with real-time property search and valuation estimates. Complex Supabase PostgreSQL queries for geospatial filters were vulnerable to aggressive scraping. Shifting rate checks to the network edge stopped unauthorized automated scrapers before they ever reached the database.

Setting Up Edge Rate Limiting with Upstash Redis and Next.js

To stop DDoS attacks and brute-force traffic, your rate limiter needs to run on edge runtimes (such as Vercel Edge Middleware or Cloudflare Workers) and communicate with a low-latency database over HTTP REST rather than stateful TCP connections.

Upstash Redis works well here because it exposes an HTTP-based REST API that runs cleanly in lightweight edge environments without connection pool exhaustion. Here is a production-ready Next.js middleware implementation using a sliding window counter:

import { NextRequest, NextResponse } from 'next/server';
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

// Initialize Upstash Redis with edge-friendly REST client
const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL!,
  token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});

// Create a sliding window limiter: 20 requests per 10 seconds
const ratelimit = new Ratelimit({
  redis,
  limiter: Ratelimit.slidingWindow(20, '10 s'),
  analytics: true,
  prefix: '@edge-limiter',
});

export async function middleware(request: NextRequest) {
  // Target only public API routes
  if (!request.nextUrl.pathname.startsWith('/api/')) {
    return NextResponse.next();
  }

  // Extract client IP address safely
  const ip = request.ip ?? request.headers.get('x-forwarded-for')?.split(',')[0] ?? '127.0.0.1';
  
  // Limit based on client IP
  const { success, limit, remaining, reset } = await ratelimit.limit(ip.trim());

  if (!success) {
    return new NextResponse(
      JSON.stringify({
        error: 'Too Many Requests',
        message: 'Rate limit exceeded. Please wait before retrying.',
      }),
      {
        status: 429,
        headers: {
          'Content-Type': 'application/json',
          'X-RateLimit-Limit': limit.toString(),
          'X-RateLimit-Remaining': remaining.toString(),
          '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;
}

export const config = {
  matcher: '/api/:path*',
};
Sliding Window vs Fixed Window
Fixed window algorithms reset counters at round clock intervals (e.g., top of the minute). An attacker can send 20 requests at 11:59:59 and another 20 at 12:00:00, pushing 40 requests in two seconds. The sliding window algorithm looks back at the preceding interval to keep request flow steady.

DDoS Defense Layers Beyond Simple IP Throttling

IP-based throttling alone cannot stop distributed attacks because modern botnets cycle through thousands of residential proxy IP addresses. A reliable defensive strategy requires layered checks at the edge network:

  • Header Validation: Drop requests immediately if standard headers like User-Agent or Accept are missing, or if they match known scraping utility defaults.
  • Payload Size Constraints: Reject POST/PUT payloads larger than 50KB directly at the edge to prevent memory bloat on compute nodes.
  • Challenge Handshakes: Use Cloudflare Turnstile or similar invisible challenges on sensitive endpoints to differentiate human users from headless scripts.
  • Tiered Limits: Apply tighter limits (e.g., 5 requests per minute) on write operations like form submissions or password resets, while allowing higher thresholds on cached read endpoints.

Handling Edge Cache Shields for Static and Dynamic APIs

Rate limiting is only half the battle. If your API returns data that changes infrequently, pairing your rate limiter with an Edge Cache Shield via the Cache-Control s-maxage directive cuts database load down to almost zero.

When building high-traffic endpoints, setting 'Cache-Control: public, s-maxage=60, stale-while-revalidate=300' lets the edge network serve cached responses directly from edge memory. The request never touches your rate-limiting Redis store or your database, absorbing volumetric read attacks automatically.

Need Help Hardening Your Web Application?

Securing web applications requires practical engineering choices that prevent downtime without degrading user experience. Explore my [Full-Stack Web Development Services](/services) to see how I build secure, high-speed applications, or [get in touch with Aafaque](/contact) directly to discuss your project security requirements.

Frequently Asked Questions

Why should rate limiting happen at the edge instead of the origin server?

When rate limiting runs on your primary server or database, abusive traffic still consumes compute cycles, memory, and database connections. Placing rate limiting on edge workers blocks malicious traffic at the CDN point of presence, preventing origin load and saving serverless execution costs.

Which rate-limiting algorithm is best for public APIs?

The sliding window counter algorithm is ideal. Unlike a fixed window counter, which allows twice the limit of requests across window boundaries, sliding window counters smooth out traffic spikes and stop boundary-burst exploits.

How do you identify clients behind shared NAT or dynamic IPs?

Use a compound key. Combine the client IP (from CF-Connecting-IP or X-Forwarded-For) with user agent hashes, API tokens, or session IDs. For anonymous public routes, prioritize IP plus browser fingerprinting headers.

← Back to all articles