Protection against brute force login attacks on Cloudflare Pages.
- Limit: 10 attempts per 15 minutes
- Lockout: 30 minutes after exceeding limit
- Purpose: Prevent automated attacks from single IP
- Limit: 5 failed attempts per 15 minutes
- Lockout: 15 minutes after exceeding limit
- Feedback: Users see remaining attempts
- Purpose: Protect individual accounts from credential stuffing
- Failed attempts logged to console (viewable in Cloudflare dashboard)
- Warnings triggered at 5+ attempts from same IP
- Successful logins logged for audit
Edit server/utils/rateLimit.ts:
const CONFIG = {
IP_MAX_ATTEMPTS: 10, // Max attempts per IP
IP_WINDOW_MS: 15 * 60 * 1000, // 15 minutes window
IP_LOCKOUT_MS: 30 * 60 * 1000, // 30 minutes lockout
ACCOUNT_MAX_ATTEMPTS: 5, // Max per account
ACCOUNT_WINDOW_MS: 15 * 60 * 1000,
ACCOUNT_LOCKOUT_MS: 15 * 60 * 1000,
}Current Implementation: In-memory storage (per worker instance)
Limitation: Each Cloudflare worker instance has its own memory. If requests are distributed across multiple workers, rate limiting may not work perfectly.
Solutions for Production:
// Use Workers KV for distributed storage
const attempts = await env.RATE_LIMIT_KV.get(`ip:${ip}`)
await env.RATE_LIMIT_KV.put(`ip:${ip}`, count, { expirationTtl: 900 })- Perfect for rate limiting with guaranteed consistency
- Requires Workers Paid plan
- Store attempts in existing D1 database
- Trade-off: Slower but persistent
- Set up in Cloudflare Dashboard → Security → Rate Limiting
- No code changes needed
- Most reliable for production
| Scenario | HTTP Status | Message |
|---|---|---|
| IP rate limited | 429 | "Too many login attempts. Try again in X minutes" |
| Account locked | 423 | "Account locked. Try again in X minutes" |
| Invalid credentials | 401 | "Invalid credentials. X attempts remaining" |
-
View Logs:
- Cloudflare Dashboard → Pages → [Your Site] → Logs
- Real-time logs show console output
-
Security Events:
- Search for
[Security]prefix - Filter by status codes 429, 423
- Search for
-
Set up Notifications:
- Cloudflare → Notifications
- Alert on high 4xx error rates
-
Add Cloudflare Rate Limiting Rule:
Path: /api/auth/login Requests: 5 per minute per IP Action: Block for 15 minutes -
Enable Bot Fight Mode:
- Cloudflare Dashboard → Security → Bots
- Blocks automated attacks
-
Set up Web Application Firewall (WAF):
- Pre-configured rules for common attacks
- Custom rules for suspicious patterns
Test locally (works with in-memory storage):
# Test account lockout (5 attempts)
for i in {1..6}; do
curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"wrong"}'
echo ""
done
# Should see lockout message on 6th attempt✅ Local Development: Full protection
- Works within single worker instance
- May not block across distributed workers
- Recommend adding Cloudflare WAF rules
For complete protection on Cloudflare Pages:
- Short term: Add Cloudflare Rate Limiting rules (no code)
- Long term: Migrate to Cloudflare KV storage (requires code update)
// Example: Upgrade to KV
export default defineEventHandler(async (event) => {
const env = event.context.cloudflare.env
const attempts = await env.RATE_LIMIT.get(`ip:${ip}`)
// ... rest of logic
})