What Is a DDoS Attack?
A DDoS attack floods your server with massive amounts of illegitimate traffic — sourced from thousands of compromised machines (botnets) — with the sole goal of exhausting your resources until legitimate users can no longer reach your site.
There are three main categories:
- Volumetric attacks — Saturate your bandwidth (e.g., UDP floods, DNS amplification)
- Protocol attacks — Exploit weaknesses in network layers (e.g., SYN floods, ping of death)
- Application-layer attacks — Target your web server or app logic (e.g., HTTP floods, Slowloris)
The good news? A layered defense strategy using modern tools makes your site dramatically harder to take down.
1. Cloudflare — Your First Line of Defense
Cloudflare is arguably the most powerful tool available for DDoS mitigation, especially on its free tier.
When you proxy your domain through Cloudflare, all incoming traffic passes through their global network before reaching your origin server. This means:
- Anycast routing absorbs and diffuses attack traffic across 300+ data centers worldwide
- Your origin server's real IP is hidden — attackers can't bypass Cloudflare to hit you directly
- Cloudflare's automatic DDoS protection detects and mitigates most Layer 3/4/7 attacks in under 3 seconds
Basic Cloudflare Setup
# 1. Add your domain to Cloudflare
# 2. Update your domain's nameservers to Cloudflare's
# 3. Enable "Proxied" (orange cloud) for your DNS A/CNAME records
Recommended Settings
// Cloudflare Dashboard → Security → Settings
{
"security_level": "medium", // Challenges suspicious IPs
"bot_fight_mode": true, // Blocks known bad bots
"ddos_protection": "high", // Aggressive mitigation
"under_attack_mode": false // Enable only during active attacks
}
Pro tip: If you're under an active DDoS, toggle "I'm Under Attack!" mode in the Cloudflare dashboard. It adds a JavaScript challenge to every visitor, blocking most automated traffic instantly.
2. Rate Limiting — Throttle Abusive Clients
Rate limiting prevents any single IP (or group of IPs) from overwhelming your endpoints by capping how many requests they can make in a time window.
At the Application Level (Next.js)
You can implement rate limiting directly in your Next.js API routes using libraries like upstash/ratelimit with Redis:
// lib/ratelimit.ts
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
export const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(100, "1 m"), // 100 requests per minute
analytics: true,
});
// app/api/contact/route.ts
import { ratelimit } from "@/lib/ratelimit";
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const ip = req.headers.get("x-forwarded-for") ?? "127.0.0.1";
const { success, limit, remaining } = await ratelimit.limit(ip);
if (!success) {
return NextResponse.json(
{ error: "Too many requests. Please try again later." },
{
status: 429,
headers: {
"X-RateLimit-Limit": String(limit),
"X-RateLimit-Remaining": String(remaining),
"Retry-After": "60",
},
}
);
}
// Handle the actual request...
return NextResponse.json({ message: "OK" });
}
At the Infrastructure Level (nginx)
For self-hosted deployments, nginx's limit_req module is highly effective:
# /etc/nginx/nginx.conf
http {
# Define a rate limit zone: 10MB memory, 20 requests/second per IP
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=20r/s;
server {
location /api/ {
limit_req zone=api_limit burst=50 nodelay;
limit_req_status 429;
# ... proxy settings
}
}
}
Key rate limiting targets:
| Endpoint Type | Suggested Limit |
|---|
| Login / Auth | 5 requests / minute |
| Contact Forms | 10 requests / minute |
| General API | 100 requests / minute |
| Static Assets | No limit (handled by CDN) |
3. Caching — Reduce Attack Surface at the Origin
Caching is one of the most underrated DDoS defenses. If your content is served from a CDN edge cache, requests never hit your origin server — meaning attackers are effectively punching a wall of cached copies.
CDN-Level Caching (Cloudflare)
Configure aggressive cache rules for static and semi-static content:
// Cloudflare Cache Rules
{
"rule": "Cache Everything",
"match": "*.html, *.css, *.js, *.png, *.jpg",
"edge_ttl": "1 month",
"browser_ttl": "1 day"
}
Use Cache-Control headers to instruct CDNs to cache your responses:
// app/api/posts/route.ts
export async function GET() {
const posts = await fetchPosts();
return NextResponse.json(posts, {
headers: {
// Cache at the edge for 1 hour, stale-while-revalidate for 24h
"Cache-Control": "public, s-maxage=3600, stale-while-revalidate=86400",
},
});
}
// For Next.js page-level caching
// app/blog/page.tsx
export const revalidate = 3600; // ISR: revalidate every hour
What to Cache vs. What Not to Cache
✅ Cache: Static pages, blog posts, product listings, images, JS/CSS bundles
❌ Don't: Authentication endpoints, checkout flows, user dashboards, payment APIs
4. Load Balancing — Distribute and Survive
Load balancers distribute traffic across multiple server instances. During a DDoS attack, this means the load is spread rather than concentrated — no single node gets overwhelmed.
Key Strategies
Horizontal scaling — Run multiple instances of your app behind a load balancer:
# docker-compose.yml (simplified example)
services:
app:
image: my-nextjs-app
deploy:
replicas: 4 # 4 app instances
nginx:
image: nginx:alpine
ports:
- "80:80"
depends_on:
- app
# nginx load balancer config
upstream nextjs_cluster {
least_conn; # Route to the instance with fewest active connections
server app_1:3000;
server app_2:3000;
server app_3:3000;
server app_4:3000;
}
server {
listen 80;
location / {
proxy_pass http://nextjs_cluster;
}
}
Auto-scaling — Cloud platforms like AWS, GCP, and Vercel automatically spin up new instances when traffic spikes. On Vercel (the natural home for Next.js), this happens transparently.
Health checks — Ensure your load balancer automatically removes unhealthy instances:
upstream nextjs_cluster {
server app_1:3000 max_fails=3 fail_timeout=30s;
server app_2:3000 max_fails=3 fail_timeout=30s;
}
5. Web Application Firewall (WAF) — Filter Malicious Requests
A WAF inspects HTTP traffic and blocks requests that match known attack signatures before they reach your application.
Cloudflare WAF
Cloudflare's WAF (available on paid plans) includes thousands of managed rules:
Cloudflare Dashboard → Security → WAF → Managed Rules
→ Enable: Cloudflare Managed Ruleset
→ Enable: Cloudflare OWASP Core Ruleset
You can also write custom WAF rules to block specific patterns:
# Block requests with suspicious User-Agents
(http.user_agent contains "sqlmap") or
(http.user_agent contains "nikto") or
(http.user_agent eq "") → Block
# Block requests from specific countries (if your audience is geo-specific)
(ip.geoip.country in {"CN" "RU" "KP"}) and
(not ip.geoip.country in {"US" "BR" "GB"}) → Challenge
Self-Hosted WAF with ModSecurity
For self-hosted nginx setups:
# Install: apt install libmodsecurity3 libmodsecurity-dev
# /etc/nginx/nginx.conf
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
# Enable OWASP Core Rule Set (CRS)
git clone https://github.com/coreruleset/coreruleset /etc/nginx/modsecurity/crs
cp /etc/nginx/modsecurity/crs/crs-setup.conf.example \
/etc/nginx/modsecurity/crs/crs-setup.conf
WAF Rules for Next.js Specifically
// middleware.ts — Application-layer WAF logic in Next.js
import { NextRequest, NextResponse } from "next/server";
const BLOCKED_USER_AGENTS = [/sqlmap/i, /nikto/i, /masscan/i, /zgrab/i];
const SUSPICIOUS_PATHS = [/\.php$/, /wp-admin/, /\.env/, /\.git/];
export function middleware(req: NextRequest) {
const ua = req.headers.get("user-agent") ?? "";
const path = req.nextUrl.pathname;
// Block known bad bots
if (BLOCKED_USER_AGENTS.some((pattern) => pattern.test(ua))) {
return new NextResponse("Forbidden", { status: 403 });
}
// Block suspicious path traversal attempts
if (SUSPICIOUS_PATHS.some((pattern) => pattern.test(path))) {
return new NextResponse("Not Found", { status: 404 });
}
return NextResponse.next();
}
export const config = {
matcher: "/((?!_next/static|_next/image|favicon.ico).*)",
};
Putting It All Together — Defense in Depth
No single tool is enough. The most resilient architecture layers all five strategies:
Internet Traffic
│
▼
┌─────────────┐
│ Cloudflare │ ← Absorbs volumetric attacks, hides origin IP
│ (CDN/WAF) │ ← WAF filters malicious HTTP requests
└──────┬──────┘
│ Clean traffic only
▼
┌─────────────┐
│ nginx │ ← Rate limiting, load balancing, TLS termination
│ (Reverse │
│ Proxy) │
└──────┬──────┘
│
▼
┌───────────────────────────┐
│ App Instances (Next.js) │ ← Application-level rate limiting
│ [app_1] [app_2] [app_3] │ ← Caching headers, middleware WAF
└───────────────────────────┘
│
▼
┌─────────────┐
│ Database │ ← Connection pooling, query limits
└─────────────┘
Conclusion
DDoS protection is not a single product you buy — it's a mindset of layered resilience. Start with Cloudflare to absorb the bulk of attacks, add rate limiting to protect your API, cache aggressively to keep origin load low, distribute traffic with a load balancer, and use a WAF to filter out malicious patterns before they ever touch your code.
With these layers in place, your Next.js application will be able to withstand all but the most sophisticated and well-resourced attacks — and even then, you'll have the observability to detect and respond quickly.