CloudflareMódulo III · Procesos y servicios · Capítulo 9 de 15Nivel: BásicoEn desarrollo

9. Imágenes

Cómo redimensionar y convertir imágenes con Cloudflare Images desde R2 o cualquier origen, con URL o con el binding, y cuánto cuesta cada transformación.

En desarrollo. La publico antes de terminarla porque ya puede servirte; la iré completando y anotaré los cambios al final.

Víctor Rodríguez4 min de lectura

Probado conTypeScript 7.0Wrangler 4.157Vitest 5.1

Una foto grande se convierte en varias copias cada vez más pequeñas.

Qué vas a aprender

  • Que las fotos pesen poco y carguen rápido en cualquier móvil.
  • Crear miniaturas automáticamente al subir una imagen.
  • Cuánto cuesta y cuándo no hace falta.

Conocimientos iniciales

Si alguna de estas preguntas no te suena, ábrela: está explicada desde cero.

  • ¿Qué son WebP y AVIF?

    Formatos de imagen modernos que pesan mucho menos que JPEG o PNG con la misma calidad. Todos los navegadores actuales los entienden.

  • ¿Qué es la caché?Repaso

    Guardar una respuesta para no volver a calcularla. Si cien personas piden la misma lista en un minuto, se calcula una vez y las otras noventa y nueve reciben la copia.

    Lo vimos en el capítulo 4.

  • ¿Qué es el almacenamiento de objetos?Repaso

    Un almacén para archivos de cualquier tamaño (fotos, PDF, vídeos), cada uno con su nombre. No es una carpeta de tu ordenador: se accede por internet. R2 de Cloudflare y S3 de AWS son de este tipo.

    Lo vimos en el capítulo 6.

Una foto de móvil pesa 4 MB; en una lista de notas se ve a 300 píxeles de ancho y debería pesar 30 KB. Cloudflare Images hace esa conversión al vuelo, sirve el formato que mejor soporta cada navegador (AVIF, WebP) y cachea el resultado. En este capítulo lo conecto con los adjuntos de R2.

Para quién es y qué necesitas

Para quien sirve imágenes de usuarios o de su web y quiere que pesen poco sin guardar diez tamaños de cada una.

Antes de empezar:

  • Imágenes en R2 (capítulo anterior) o en cualquier URL.
  • Las transformaciones activadas en la zona: Images › Transformations › Enable for zone.

Al terminar tendrás miniaturas en AVIF generadas al vuelo o al subir, con caché, y sabrás cuándo optimizar en el build y no en Cloudflare.

Dos formas de trabajar con Images

flowchart LR
    subgraph T["Transformar (lo que más uso)"]
        N1[Navegador] -->|/cdn-cgi/image/width=400| CF1[Images]
        CF1 -->|original| R2[(R2 u otro origen)]
    end
    subgraph A["Almacenar en Images"]
        S[Subida] --> IMG[(Images)]
        IMG -->|variantes| N2[Navegador]
    end
Modo Dónde está el original Cuándo
Transformar En R2, en tu web o en otro servidor Ya tienes los archivos; quieres miniaturas y formatos modernos
Almacenar en Images En Cloudflare Images Quieres que Cloudflare gestione también el almacenamiento, con variantes predefinidas

En mis proyectos uso casi siempre transformar: los originales se quedan en R2, que ya pago, y solo cobra Images por las transformaciones.

Transformar con la URL

Con las transformaciones activadas, cualquier imagen de tu zona se puede pedir así:

https://mydomain.dev/cdn-cgi/image/width=400,quality=80,format=auto/attachments/photo.jpg
Parámetro Ejemplo Qué hace
width / height width=400 Tamaño máximo
fit fit=cover Cómo encajar: scale-down, contain, cover, crop, pad
format format=auto AVIF o WebP según lo que acepte el navegador
quality quality=80 Compresión (1–100)
gravity gravity=face Hacia dónde recortar (auto, face, coordenadas)

En el HTML, con varios tamaños para que el navegador elija:

Componente de miniatura
<img
src="/cdn-cgi/image/width=400,format=auto/attachments/photo.jpg"
srcset="
/cdn-cgi/image/width=400,format=auto/attachments/photo.jpg 400w,
/cdn-cgi/image/width=800,format=auto/attachments/photo.jpg 800w
"
sizes="(max-width: 640px) 100vw, 400px"
width="400"
height="300"
loading="lazy"
alt="Photo attached to the note"
/>

Transformar desde el Worker con el binding

Cuando el Worker tiene que decidir (comprobar permisos de un adjunto privado, recortar para un avatar), uso el binding:

wrangler.jsonc (extracto)
{
"images": { "binding": "IMAGES" }
}
src/thumbnails.ts
const WIDTHS = new Set([200, 400, 800]);
export async function thumbnail(env: Env, key: string, width: number) {
if (!WIDTHS.has(width)) {
return new Response("Size not allowed", { status: 400 });
}
const original = await env.ATTACHMENTS.get(key);
if (!original) return new Response("Not found", { status: 404 });
const result = await env.IMAGES.input(original.body)
.transform({ width, fit: "scale-down" })
.output({ format: "image/avif", quality: 75 });
const response = result.response();
response.headers.set("Cache-Control", "public, max-age=86400");
return response;
}

Generar miniaturas al subir

Para listados con muchas imágenes, prefiero generar las miniaturas una vez, al subir el archivo, con la cola del capítulo de segundo plano, y guardarlas en R2. Así la transformación se paga una vez y no en cada mes en que se pide.

flowchart TB
    U[Subida] --> R[(R2: original)]
    U --> Q[(Cola)]
    Q --> C[Consumidor]
    C --> I[Images: 200 y 800 px]
    I --> R2[(R2: miniaturas)]
src/consumer.ts (extracto)
async function createThumbnails(env: Env, key: string) {
for (const width of [200, 800]) {
const original = await env.ATTACHMENTS.get(key);
if (!original) return;
const r = await env.IMAGES.input(original.body)
.transform({ width })
.output({ format: "image/webp" });
await env.ATTACHMENTS.put(`${key}@${width}.webp`, r.image(), {
httpMetadata: { contentType: "image/webp" },
});
}
}

Sitios estáticos: optimizar en el build

En una web de contenido como este blog no hace falta Images: Astro (y la mayoría de generadores) optimizan las imágenes durante el build con <Image> y las sirven como estáticos gratis. Images compensa cuando las imágenes las suben los usuarios o cambian a menudo.

Cuánto cuesta

Cuánto cuesta al mes

Supuestos: 2.000 fotos subidas al mes, 2 tamaños cada una (4.000 transformaciones únicas)

ConceptoImporte
Transformaciones5.000 únicas al mes incluidas0,00 $
Almacenamiento de originales en R2Unos 20 GB más allá de lo gratuito0,30 $
Total al mes0,30 $

Precios consultados el 3 de noviembre de 2026. Pueden cambiar.

Concepto Gratuito Pago
Transformaciones únicas 5.000 al mes (después, error 9422; lo ya cacheado sigue funcionando) 5.000 incluidas; 0,50 $ por cada 1.000 más
Imágenes almacenadas en Images — 5 $ por cada 100.000
Imágenes entregadas desde Images — 1 $ por cada 100.000

Una transformación única es una imagen de origen con un conjunto de parámetros; repetirla en el mismo mes cuenta una vez, y format y quality no cuentan como parámetros distintos.

Resumen

  • Originales en R2; transformaciones con URL para el frontend y con binding desde el Worker.
  • Restringe orígenes y tamaños para no pagar transformaciones que no has pedido.
  • Para listados, miniaturas generadas una vez al subir.
  • Siguiente capítulo: IA en el borde.

Recursos

¿Te ha servido? Esto forma parte de un libro que estoy escribiendo: Cloudflare de cero a producción. Ver el índice provisional →