effidevFlutter · Edge de Cloudflare · Optimización de costes en la nube
Español

Migración de AWS S3 a Cloudflare R2: Reducción del 99%

Migración de AWS S3 a Cloudflare R2 para reducir costes de transferencia

Para las empresas que almacenan y operan archivos multimedia de gran tamaño, streaming de vídeo y datasets masivos de IA en la nube, la factura mensual de AWS S3 es motivo de constante preocupación. Esto se debe a que más que el coste del almacenamiento en sí, los costes de salida de datos (Data Transfer Out, $0,09 por GB) generados cuando los usuarios descargan archivos o transfieren datos a CDN o servidores externos representan del 70% al 90% del coste total de la nube.

Cloudflare R2, el almacenamiento de objetos de Cloudflare, ha cambiado por completo las reglas del juego. R2 ofrece costes de salida (tarifas de transferencia de ancho de banda) 100% gratuitos ($0/GB), y la tarifa de almacenamiento también es aproximadamente un 35% más económica que la de S3 Standard ($0.015/GB al mes).

Además, R2 es 100% compatible con la API de S3 existente, por lo que se puede reemplazar al instante sin modificar el código, simplemente cambiando la URL del endpoint. También incluye una canalización de migración diferida (Lazy Migration / Super Slurp) que permite migrar terabytes (TB) de datos sin interrupción del servicio.

Este artículo aborda una comparación detallada de la estructura de costes entre S3 y R2, cómo cambiar los endpoints a R2 en un entorno de AWS SDK, una arquitectura de migración diferida sin caídas y una canalización de optimización de imágenes y multimedia en el edge global que combina Cloudflare Workers con R2 mediante código de configuración práctico.

Resumen clave

  • Coste de salida $0/GB: Por mucho que se transfieran datos a Internet o a una nube externa, la tarifa de ancho de banda es de $0. Es la mejor opción para distribución de contenido multimedia y compartición de datasets de IA.
  • Reducción del 35% en almacenamiento: En comparación con los $0.023/GB de S3 Standard, R2 cuesta $0.015/GB, lo que resulta más económico desde el coste base de almacenamiento.
  • Compatibilidad del 100% con S3 API: Permite usar directamente el AWS SDK (@aws-sdk/client-s3) y funciona al instante cambiando únicamente la configuración de endpoint a la dirección de R2.
  • Migración diferida (Lazy Migration): Sin tiempo de inactividad del servicio, sincroniza automáticamente los objetos no encontrados desde S3 hacia R2 en el momento de la solicitud del usuario, iniciando la migración en cuestión de segundos.
  • Vinculación edge de Workers + R2: Al realizar una vinculación nativa con Cloudflare Workers, se pueden renderizar datos de almacenamiento al instante en el edge con un sobrecoste de salto de red (Hop) de 0 ms y ejecutar transformaciones de imágenes.

1. Comparación de costes: AWS S3 vs Cloudflare R2 con simulación de factura real

Esta es la simulación mensual a fecha de agosto de 2026 para un servicio de multimedia/IA que almacena 100 TB de datos y genera 500 TB de salida (transferencia) al mes. Los cálculos se basan en la tarifa de AWS S3 y la documentación oficial de precios de Cloudflare R2.

Concepto AWS S3 Standard Cloudflare R2 Diferencia de coste
Almacenamiento (100TB) $2,300 ($0.023/GB) $1,500 ($0.015/GB) Ahorro de $800 (35%)
Transferencia de salida (500TB) $44,910 ($0.089/GB) $0 ($0/GB) Ahorro de $44,910 (100%)
Operaciones Clase A (10M escrituras) $50 ($5.00/1M) $45 ($4.50/1M) Ahorro de $5
Operaciones Clase B (100M lecturas) $40 ($0.40/1M) $36 ($0.36/1M) Ahorro de $4
Factura mensual total de la nube $47,300 $1,581 Ahorro mensual de $45,719 (¡Reducción del 96.6%!)

Cuanto mayor sea la proporción de transferencia de salida en un servicio, más se acerca la tasa de ahorro de costes al 95%~99%. Esta es la razón más convincente por la que las empresas que sufrían el bloqueo de proveedor (Vendor Lock-in) por los costes de salida de AWS están migrating a R2.

2. Cero modificaciones de código: Conexión del endpoint de R2 en AWS SDK

Dado que R2 soporta la API compatible con S3, se puede reutilizar el código existente de AWS SDK en Node.js, Python o Go.

// Antes (AWS S3)
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';

const s3Client = new S3Client({
  region: 'us-east-1',
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
});

// Después (Cloudflare R2) — ¡Funciona al 100% solo reemplazando el endpoint!
const r2Client = new S3Client({
  region: 'auto', // R2 se especifica como 'auto' al ser una región única global
  endpoint: `https://${process.env.CF_ACCOUNT_ID}.r2.cloudflarestorage.com`,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID!,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
  },
});

A tener en cuenta: R2 no tiene restricciones de regiones específicas de AWS S3 (como us-east-1), sino que ofrece un enrutamiento global óptimo automático hacia la ubicación más cercana a la solicitud desde los más de 300 nodos edge de Cloudflare.

3. Patrón de ‘migración diferida (Lazy Migration)’ sin interrupción del servicio

Transferir terabytes de datos de S3 a R2 de una sola vez mediante una migración “Big Bang” lleva mucho tiempo y supone un riesgo de inactividad del servicio. Al utilizar el patrón de migración diferida (Lazy Migration), los datos se van cargando en R2 en tiempo real a medida que se reciben las solicitudes, sin interrumpir el servicio.

[Solicitud del cliente] ──► [Cloudflare Workers]

                                  ├─► 1. Consulta objeto R2 (Cache Hit) ──► Devuelve archivo al instante

                                  └─► 2. Si no existe (Cache Miss) 

                                            ├─► Descarga archivo del origen S3
                                            ├─► Guarda en el bucket R2 (sincronización asíncrona)
                                            └─► Devuelve archivo al cliente

Código práctico de Workers para la migración diferida

export interface Env {
  MY_R2_BUCKET: R2Bucket;
  S3_ORIGIN_URL: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const key = url.pathname.slice(1);

    // 1. Consultar archivo en el bucket R2
    let object = await env.MY_R2_BUCKET.get(key);

    if (object === null) {
      // 2. Si el archivo no está en R2, se lee desde el origen S3 existente (Lazy Fetch)
      const s3Response = await fetch(`${env.S3_ORIGIN_URL}/${key}`);
      if (!s3Response.ok) {
        return new Response('File Not Found', { status: 404 });
      }

      const bodyBuffer = await s3Response.arrayBuffer();

      // 3. Guardar en segundo plano de forma asíncrona el archivo recibido de S3 en el bucket R2
      env.MY_R2_BUCKET.put(key, bodyBuffer, {
        httpMetadata: s3Response.headers,
      });

      // 4. Devolver la respuesta inmediatamente al cliente
      return new Response(bodyBuffer, {
        headers: s3Response.headers,
      });
    }

    // Respuesta inmediata en caso de acierto en R2 (coste de salida $0)
    const headers = new Headers();
    object.writeHttpMetadata(headers);
    headers.set('etag', object.httpEtag);

    return new Response(object.body, { headers });
  },
};

Al aplicar este patrón, los datos activos a los que acceden los usuarios se transfieren progresivamente a R2, reduciendo drásticamente los costes de salida de S3 desde el primer día de la migración.

4. Canalización de imágenes dinámicas con R2 + Cloudflare Image Resizing

Combinar imágenes de alta resolución originales almacenadas en R2 con Cloudflare Workers permite reemplazar servicios externos costosos como la optimización de imágenes de Next.js o Cloudinary. Es una aplicación práctica tratada en la Guía de comparación de costes de Cloudflare Images.

// Conversión a WebP y redimensionamiento en tiempo real de imágenes de R2 en Workers
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const width = parseInt(url.searchParams.get('w') || '800');
    const quality = parseInt(url.searchParams.get('q') || '80');
    const key = url.pathname.slice(1);

    // Vincular opciones de Cloudflare Image Resizing con el objeto R2
    const options = {
      cf: {
        image: {
          width,
          quality,
          format: 'webp',
        },
      },
    };

    const imageObject = await env.MY_R2_BUCKET.get(key);
    if (!imageObject) return new Response('Not Found', { status: 404 });

    return new Response(imageObject.body, {
      headers: {
        'content-type': 'image/webp',
        'cache-control': 'public, max-age=31536000, immutable',
      },
    });
  },
};

Si se utiliza junto con la Guía de migración de AWS Lambda a Cloudflare Workers para reducir costes de salida, se pueden minimizar los costes de la nube tanto en el ámbito del cómputo como en el del almacenamiento.

5. Precauciones y buenas prácticas al utilizar R2

Lista de verificación Descripción y buenas prácticas
Conexión de dominio personalizado Vincular un dominio personalizado al bucket R2 aplica automáticamente Cloudflare CDN Caching, reduciendo incluso los costes de operaciones de lectura Clase B.
Precaución con la exposición de buckets públicos El acceso público a buckets puede provocar descargas no autorizadas, por lo que se deben controlar las autorizaciones mediante vinculaciones de Workers o URLs firmadas (Signed URLs).
Control de operaciones Clase A / Clase B Aunque el tráfico de salida en R2 es gratuito, se cobra una pequeña cantidad por el número de operaciones de lectura/escritura. Se deben configurar cabeceras de caché CDN (max-age=31536000) para minimizar las operaciones de lectura.
Sustitución de S3 Object Lock En caso de requerir funciones de cumplimiento normativo especiales de S3 (como Object Lock), se deben verificar previamente los requisitos legales.

Preguntas frecuentes

¿El coste de salida en R2 es realmente 100% gratuito?

Sí, es 100% gratuito. Incluso si se transfieren terabytes o petabytes de datos a la red pública de Internet, Cloudflare no cobra ninguna tarifa por ancho de banda.

¿Existe alguna herramienta para migrar datos masivos de un bucket S3 existente de una sola vez?

Utilizando la herramienta R2 Super Slurp en el panel de control de Cloudflare, basta con introducir las credenciales de S3 para copiar automáticamente todos los objetos del bucket S3 a R2 en segundo plano.

¿R2 ofrece una latencia de renderizado más rápida que AWS S3?

Según la ubicación del usuario, resulta más rápida. Dado que los datos en AWS S3 están fijados en una sola región específica (por ejemplo, us-east-1), la latencia para usuarios internacionales es alta; sin embargo, R2 se integra con la red edge global de más de 300 ubicaciones de Cloudflare, realizando el almacenamiento en caché y renderizado desde el edge más cercano al usuario.

¿Se puede utilizar R2 en proyectos de Next.js / React?

Sí, se puede utilizar de inmediato en cualquier framework compatible con el conector S3 de AWS SDK (Next.js, React, Remix, Astro, Express, etc.) simplemente reemplazando la propiedad endpoint.