El rate limiting es esencial para proteger tus APIs y páginas contra abusos, ataques de fuerza bruta y uso excesivo. Cloudflare Workers proporciona un binding de Rate Limiting integrado que facilita implementar rate limiting en el edge.
En esta guía, aprenderás cómo implementar rate limiting en Astro usando la API de Rate Limiting de Cloudflare, tanto globalmente a través de middleware como a nivel de endpoint.
Requisitos Previos
- Node.js 20 o posterior
- Una cuenta de Cloudflare
Crear una nueva aplicación Astro
Comencemos creando un nuevo proyecto Astro. Ejecuta el siguiente comando:
npm create astro@latest my-ratelimit-astro-appCuando se te pregunte, elige:
Use minimal (empty) templatecuando se te pregunte cómo comenzar el nuevo proyecto.Yescuando se te pregunte si instalar dependencias.Yescuando se te pregunte si inicializar un repositorio git.
Una vez hecho, muévete al directorio del proyecto:
cd my-ratelimit-astro-appnpm install wranglernpm run devLa aplicación debería estar ejecutándose en localhost:4321.
Integrar el adaptador Cloudflare en tu proyecto Astro
Para desplegar tu proyecto Astro en Cloudflare Workers y usar Cloudflare KV, necesitas instalar el adaptador de Cloudflare. Ejecuta el siguiente comando:
npx astro add cloudflareCuando se te pregunte, elige Yes para cada prompt.
Configurar el binding de Rate Limiting
Agrega el binding de rate limiting a tu wrangler.jsonc:
{ // ... "ratelimits": [ { "namespace_id": "1001", "name": "MY_RATE_LIMITER", "simple": { "limit": 100, "period": 60 } } ]}Esta configuración:
- Crea un rate limiter llamado
MY_RATE_LIMITER - Permite 100 solicitudes por 60 segundos por clave única
- Usa
namespace_idpara aislar los contadores de rate limit
Actualiza tu src/env.d.ts para agregar definiciones de TypeScript:
/// <reference types="astro/client" />
type RateLimiter = { limit: (options: { key: string }) => Promise<{ success: boolean }>}
type ENV = { MY_RATE_LIMITER: RateLimiter}
type Runtime = import('@astrojs/cloudflare').Runtime<ENV>
declare namespace App { interface Locals extends Runtime {}}Rate Limiting en Astro Middleware
Para aplicar rate limiting globalmente (o a rutas específicas), crea un archivo middleware en src/middleware.ts:
import { defineMiddleware } from 'astro:middleware'
// Rutas con rate limitingconst RATE_LIMITED_ROUTES = ['/']
export const onRequest = defineMiddleware(async (context, next) => { const { url, request, locals } = context const pathname = url.pathname
// Verificar si la ruta debe tener rate limiting const shouldRateLimit = RATE_LIMITED_ROUTES.some((route) => pathname === (route) )
if (!shouldRateLimit) { return next() }
// Omitir si el rate limiter no está disponible (desarrollo local) const rateLimiter = locals.runtime?.env?.MY_RATE_LIMITER if (!rateLimiter) { console.log('[Rate Limit] Binding no disponible, omitiendo') return next() }
// Usar IP del cliente como clave de rate limit const clientIP = request.headers.get('CF-Connecting-IP') || 'unknown'
try { const { success } = await rateLimiter.limit({ key: clientIP })
if (!success) { return new Response( JSON.stringify({ error: 'Demasiadas Solicitudes', message: 'Límite de velocidad excedido. Por favor, intenta de nuevo más tarde.', }), { status: 429, headers: { 'Content-Type': 'application/json', 'Retry-After': '60', }, } ) } } catch (error) { console.error('[Rate Limit] Error:', error) // En caso de error, permitir la solicitud (fail open) }
return next()})
Este middleware:
- Verifica si la ruta actual debe tener rate limiting
- Usa la dirección IP del cliente como clave de rate limit
- Retorna una respuesta
429 Too Many Requestscuando se excede el límite
Rate Limiting en un Endpoint de API
Para un control más granular, aplica rate limiting directamente en tus endpoints de API. Crea src/pages/api/data.ts:
import type { APIContext } from 'astro'
export async function GET({ request, locals }: APIContext) { const rateLimiter = locals.runtime?.env?.MY_RATE_LIMITER
if (rateLimiter) { const clientIP = request.headers.get('CF-Connecting-IP') || 'unknown'
const { success } = await rateLimiter.limit({ key: clientIP })
if (!success) { return new Response( JSON.stringify({ error: 'Límite de velocidad excedido' }), { status: 429, headers: { 'Content-Type': 'application/json' }, } ) } }
// Tu lógica del endpoint aquí return new Response( JSON.stringify({ message: 'Éxito', data: { timestamp: Date.now() } }), { status: 200, headers: { 'Content-Type': 'application/json' }, } )}
Este endpoint:
- Usa la dirección IP del cliente como clave de rate limit
- Retorna una respuesta
429 Rate Limit Exceededcuando se excede el límite
Claves de Rate Limit Personalizadas
Puedes usar diferentes claves para diferentes estrategias de rate limiting:
// Rate limit por ID de usuario (para rutas autenticadas)const userId = locals.user?.idconst { success } = await rateLimiter.limit({ key: `user:${userId}` })
// Rate limit por combinación de IP + endpointconst key = `${clientIP}:${url.pathname}`const { success } = await rateLimiter.limit({ key })
// Rate limit por clave de APIconst apiKey = request.headers.get('X-API-Key') || 'anonymous'const { success } = await rateLimiter.limit({ key: `api:${apiKey}` })Desplegar en Cloudflare Workers
Despliega tu aplicación Astro con rate limiting habilitado a producción:
# Construir el proyectonpm run build
# Desplegar en Cloudflare Workersnpx wrangler deployConclusión
Al implementar rate limiting con Cloudflare Workers en tu aplicación Astro, bloqueas efectivamente solicitudes abusivas - como ataques de fuerza bruta, uso excesivo de API e intentos de DDoS en el edge. Esto mejora tanto la seguridad como el rendimiento de tu aplicación al detener las amenazas antes de que lleguen a la lógica de tu aplicación.