From a97b261c14372c91fc3f4b09f5fa82a83f10e526 Mon Sep 17 00:00:00 2001 From: ralksta Date: Fri, 13 Mar 2026 12:55:07 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20raise=20map=20rate=20limit=2030=E2=86=92?= =?UTF-8?q?120/min,=20allow=205min=20private=20browser=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevents spurious 429s during normal browsing/refreshing. Browser now caches personalised map data for 5 minutes (private, so CDNs/proxies won't share it between users). --- app/api/map/route.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/api/map/route.ts b/app/api/map/route.ts index 6c5cfe9..feed4ac 100644 --- a/app/api/map/route.ts +++ b/app/api/map/route.ts @@ -15,12 +15,12 @@ import { checkRateLimit } from '@/lib/rate-limit'; export const dynamic = 'force-dynamic'; export async function GET(request: NextRequest) { - // Rate limit: 30 requests/minute per IP + // Rate limit: 120 requests/minute per IP (map data is cached client-side; this guards against abuse) const ip = request.headers.get('x-forwarded-for')?.split(',')[0].trim() ?? request.headers.get('x-real-ip') ?? '127.0.0.1'; - const rl = checkRateLimit(ip, 30); + const rl = checkRateLimit(ip, 120); if (!rl.success) { return NextResponse.json( { error: 'Too many requests' }, @@ -28,7 +28,7 @@ export async function GET(request: NextRequest) { status: 429, headers: { 'Retry-After': String(Math.ceil((rl.resetAt - Date.now()) / 1000)), - 'X-RateLimit-Limit': '30', + 'X-RateLimit-Limit': '120', 'X-RateLimit-Remaining': '0', }, }, @@ -74,7 +74,9 @@ export async function GET(request: NextRequest) { return NextResponse.json(publicLocations, { headers: { - 'Cache-Control': `private, no-cache, no-store, must-revalidate`, // Don't cache personalized map data publicly + // Private 5-minute browser cache: reduces re-fetches during a session + // while still preventing shared/CDN caching of personalised data. + 'Cache-Control': 'private, max-age=300, must-revalidate', }, }); }