wandres.dev
STATIC ASSETS · la paridad con Pages

Servir un sitio estático desde un Worker

El caso más simple de la plataforma: declarar el bloque `assets` en `wrangler.jsonc`, apuntarlo a un directorio y dejar que la red de Cloudflare sirva esos archivos desde el edge sin ejecutar una sola línea de código. El directorio como raíz del sitio, el mapeo de archivos a rutas que gobierna `html_handling`, y el ciclo de `wrangler dev` a `wrangler deploy`.

⏱ 15 min

Un sitio estático es el caso degenerado de una respuesta HTTP: bytes que no cambian de una petición a otra —HTML, CSS, JavaScript, tipografías, imágenes— y que, por tanto, no necesitan que nadie los compute, solo que alguien los entregue rápido y cerca del usuario. Durante años eso obligaba a levantar un servidor o a contratar un producto aparte. Desde marzo de 2026 basta un Worker: declaras un bloque assets en wrangler.jsonc, lo apuntas a una carpeta, y la red de Cloudflare sirve esos archivos desde cientos de ciudades sin que tu código llegue a arrancar. Esta lección instala el modelo mínimo —un directorio, un manifiesto, dos comandos— sobre el que se construye todo lo demás del nivel.

🎯 Al terminar esta lección sabrás
  • Declarar el bloque assets en wrangler.jsonc con su campo directory.
  • Entender cómo el árbol de archivos se convierte en rutas y qué gobierna html_handling.
  • Servir el sitio en local con wrangler dev y publicarlo con wrangler deploy.
  • Comprender por qué una petición a un asset estático no cuenta como invocación del Worker.

Un sitio sin servidor

Servir un archivo estático no requiere lógica: requiere un distribuidor de bytes veloz y próximo al usuario, que es exactamente lo que la red de Cloudflare sabe hacer mejor que nada. El bloque assets convierte tu proyecto de Worker en ese distribuidor.

La configuración de un sitio puramente estático cabe en unas pocas líneas y tiene una ausencia notable —no hay campo main, porque no hay código que ejecutar—:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "mi-sitio",
  "compatibility_date": "2026-03-01",
  "assets": {
    "directory": "./dist"
  }
}

Ese manifiesto declara todo lo necesario: un nombre, una fecha de compatibilidad y una carpeta. El campo directory es la raíz del sitio; suele apuntar a la salida de tu empaquetador —./dist, ./build o ./public—, la carpeta que ya contiene el HTML final listo para servir. A partir de aquí, cada archivo dentro de ese directorio es una URL potencial, y Cloudflare se encarga del resto.

Del árbol de archivos a las rutas

Cuando ejecutas wrangler deploy, wrangler recorre directory, calcula el hash del contenido de cada archivo y sube solo los que Cloudflare aún no tiene. El almacenamiento es direccionado por contenido: dos despliegues que comparten un logo idéntico comparten el mismo objeto, y un redeploy que solo cambió una página sube esa página y nada más. Esa subida incremental es lo que hace que publicar un sitio grande cueste segundos en lugar de minutos.

El árbol de carpetas se traduce en rutas de forma directa, y unas pocas reglas gobiernan los detalles que suelen morder:

🗂️

El directorio es la raíz

dist/index.html responde en /, dist/blog/post.html en /blog/post y dist/estilos.css en /estilos.css. La estructura de carpetas es el mapa de rutas.

🔗

html_handling

Su valor por defecto, auto-trailing-slash, sirve about.html tanto en /about como en /about/ y normaliza la URL. Puedes forzar otra política si necesitas una forma canónica concreta.

🔒

Contenido, no fuentes

Se sube lo que hay en directory tal cual: es literalmente lo que cualquiera podrá descargar por URL. Apunta a la salida del build, nunca a tus fuentes.

Cómo se resuelven las extensiones .html y las barras finales lo decide html_handling. Los otros valores posibles —force-trailing-slash, drop-trailing-slash y none— existen para cuando tu sitio tiene requisitos estrictos sobre la forma canónica de las URLs, algo que importa para el SEO y para no duplicar contenido bajo dos direcciones.

flowchart LR
U[Navegador pide una ruta] --> E[Edge de Cloudflare]
E --> M{Coincide un asset}
M -->|si| S[Sirve los bytes sin invocar codigo]
M -->|no| N[not_found_handling decide la respuesta]
style S fill:#a6e3a1,color:#11111b

De wrangler dev a wrangler deploy

El ciclo de trabajo son dos comandos. wrangler dev levanta el sitio en tu máquina, sirviendo el contenido de directory en una dirección local y recargando cuando cambias un archivo; es una réplica fiel del comportamiento del edge, no una aproximación, porque usa el mismo runtime por debajo.

Cuando el resultado te convence, wrangler deploy sube los assets y publica el sitio en tu subdominio de workers.dev o en el dominio que hayas enganchado:

# Sirve el sitio en local con recarga en caliente
npx wrangler dev

# Publica los assets al edge global
npx wrangler deploy

Hay una consecuencia económica que no conviene pasar por alto y que define el modelo de 2026: las peticiones que se resuelven íntegramente con un asset estático son gratuitas. Cuando el edge encuentra el archivo pedido, lo entrega sin arrancar ningún isolate, y por tanto sin consumir una invocación de Worker. No pagas por servir tu CSS, tus imágenes ni tu HTML; solo pagarías si una petición tuviera que caer en código, algo que un sitio puramente estático nunca hace.

ℹ️
Qué archivos conviene subir

El directorio de assets debe contener el resultado de tu compilación, no tu código fuente. Si usas un framework, apunta directory a la carpeta que genera su comando de build, y añade esa carpeta a tu .gitignore. Subir por error node_modules o los fuentes no rompe el sitio, pero infla el despliegue con archivos que jamás deberían ser públicos: recuerda que el directorio de assets es, literalmente, lo que cualquiera podrá descargar por URL.

💡
El binding `ASSETS` es opcional aquí

Notarás que este manifiesto no declara binding ni main. Es correcto: para un sitio puramente estático no necesitas ni código ni un nombre en env. El binding ASSETS solo entra en juego cuando añades un Worker que quiere servir archivos de forma programática —lo verás en la lección sobre assets y Worker juntos—. Empieza por lo mínimo: un directorio basta para tener un sitio global en producción.

Lo estático es la respuesta que ya calculaste

Hay una simetría profunda escondida en la sencillez de este bloque de configuración. Toda respuesta HTTP existe en un espectro: en un extremo, la respuesta totalmente dinámica, que hay que computar de cero en cada petición porque depende del usuario, de la hora, del estado del mundo; en el otro, la respuesta totalmente estática, que es idéntica para todos y para siempre hasta el próximo despliegue. Un asset estático no es una categoría distinta de una respuesta dinámica: es su caso límite, la respuesta cuyo cómputo ya ocurrió —en tu máquina, durante el build— y que por tanto solo resta entregar. Entender esto reordena la arquitectura entera: la pregunta de diseño deja de ser “¿servidor o CDN?” y pasa a ser “¿cuándo se calcula cada byte, en build o en request?”. Cuanto más contenido empujes hacia el momento del build, más de tu sitio se sirve como bytes inertes desde la ciudad más cercana al usuario, sin isolates, sin latencia de cómputo y —esta es la parte que la plataforma de 2026 hace explícita— sin coste. La red no distingue filosóficamente entre “archivo” y “respuesta”: distingue entre lo que puede entregar sin pensar y lo que debe pensar para entregar. El bloque assets es la declaración formal de la primera categoría, y dominarlo es aprender a mover deliberadamente la frontera entre ambas, empujando hacia el build todo lo que no dependa de la petición concreta.

⚔️ Publica tu primer sitio estático
  1. Crea una carpeta dist con un index.html, una hoja de estilos y una segunda página en dist/about.html.
  2. Escribe un wrangler.jsonc mínimo con name, compatibility_date y un bloque assets que apunte a ./dist; no declares main.
  3. Ejecuta wrangler dev y comprueba que / sirve el índice y que /about sirve la segunda página sin la extensión .html.
  4. Publica con wrangler deploy y visita la URL de workers.dev; abre las herramientas de red y confirma que las respuestas llegan cacheadas desde el edge.
  5. Cambia solo el index.html, vuelve a desplegar y razona por qué la subida fue casi instantánea pese a no haber tocado el resto de archivos.