CloudflareMódulo I · Fundamentos · Capítulo 4 de 15Nivel: BásicoEn desarrollo

4. Publicar el frontend

Cómo servir el frontend desde Workers con Static Assets: Astro, SPA de React o full-stack, con la API en el mismo Worker, cabeceras, 404 y caché.

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

Víctor Rodríguez5 min de lectura

Probado conTypeScript 7.0Wrangler 4.157Vitest 5.1

Las páginas de tu web salen del portátil y la red las entrega a móviles y ordenadores.

Qué vas a aprender

  • Publicar la parte visual de tu web o aplicación.
  • Elegir entre una web sencilla, una aplicación de una página o una web con servidor.
  • Hacer que cargue rápido guardando copias de lo que no cambia.

Conocimientos iniciales

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

  • ¿Qué es el frontend?

    Lo que ve y toca el usuario: las páginas, los botones, los formularios. Se ejecuta en su navegador. Lo contrario es el backend, lo que pasa en el servidor.

  • ¿Qué es el backend?

    La parte de la aplicación que se ejecuta en el servidor: guarda y lee datos, llama a otros servicios con claves secretas, envía correos. El usuario no la ve.

  • ¿Qué es un sitio estático?

    Una web hecha solo de archivos ya preparados (HTML, CSS, imágenes) que se sirven tal cual, sin calcular nada en cada visita. Es lo más rápido y barato de publicar.

  • ¿Qué es una SPA?

    Aplicación de una sola página: el navegador descarga una vez el código y a partir de ahí dibuja cada pantalla él mismo, pidiendo solo datos a la API. Muchas apps hechas con React o con IA son así.

  • ¿Qué es la caché?

    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.

  • ¿Qué es un Worker?Repaso

    Un trozo de tu código que Cloudflare ejecuta en sus servidores cada vez que llega una petición. Arranca en milisegundos, no tienes que mantener ninguna máquina y se paga por uso.

    Lo vimos en el capítulo 3.

Este blog es un sitio estático servido desde Workers, y me cuesta lo que cuesta el dominio. En este capítulo explico cómo se sirve cualquier frontend con Static Assets: desde una web estática como esta hasta una SPA con su API o una aplicación full-stack con renderizado en servidor.

Para quién es y qué necesitas

Para quien tiene un frontend (Astro, React, Vue o HTML a mano) y quiere servirlo desde Cloudflare, con o sin API.

Antes de empezar:

  • El proyecto del capítulo anterior, o cualquier frontend que genere una carpeta de archivos (dist/).
  • Wrangler 4.

Al terminar tendrás tu frontend en Workers con Static Assets, la API en el mismo Worker si la necesitas y respuestas cacheadas donde compense.

Código de la guía en GitHub →

Tres formas de servir un frontend

flowchart TB
    subgraph A["1 · Estático"]
        A1[Petición] --> A2[Static Assets]
    end
    subgraph B["2 · SPA + API"]
        B1[Petición] --> B2{¿/api/*?}
        B2 -->|No| B3[Static Assets<br/>index.html]
        B2 -->|Sí| B4[Worker]
    end
    subgraph C["3 · Full-stack"]
        C1[Petición] --> C2{¿Archivo?}
        C2 -->|Sí| C3[Static Assets]
        C2 -->|No| C4[Worker<br/>renderiza la página]
    end
Modelo Ejemplos Ejecuta tu código Coste de las peticiones a páginas
Estático Blog, documentación, landing No Gratis
SPA + API React, Vue o Svelte con una API Solo en /api/* Gratis las páginas; la API se cobra como Worker
Full-stack Astro con SSR, React Router, Next.js con vinext En las páginas dinámicas Las páginas dinámicas se cobran como Worker

Opción 1: sitio estático

Basta con decirle a Wrangler dónde están los archivos. No hace falta main: sin código, ninguna petición ejecuta un Worker.

wrangler.jsonc
{
"name": "my-website",
"compatibility_date": "2026-11-03",
"assets": {
"directory": "./dist",
"not_found_handling": "404-page",
"html_handling": "drop-trailing-slash"
}
}
Opción Valores Qué hace
not_found_handling 404-page · single-page-application · none Qué devolver si el archivo no existe: tu 404.html, el index.html (para SPAs) o un 404 vacío
html_handling auto-trailing-slash · drop-trailing-slash · force-trailing-slash · none Cómo tratar /pagina, /pagina/ y /pagina.html

Para cabeceras y redirecciones, dos archivos en la carpeta de salida:

public/_headers
/_astro/*
Cache-Control: public, max-age=31536000, immutable
/*
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
public/_redirects
/feed /rss.xml 301
/old /new 301

Opción 2: SPA con su API en el mismo Worker

Una SPA de React o Vue necesita que cualquier ruta devuelva index.html, y la API, que /api/* llegue a tu código. Se consigue con run_worker_first:

wrangler.jsonc
{
"name": "notes",
"main": "src/index.ts",
"compatibility_date": "2026-11-03",
"assets": {
"directory": "./dist",
"binding": "ASSETS",
"not_found_handling": "single-page-application",
"run_worker_first": ["/api/*"]
},
"d1_databases": [
{ "binding": "DB", "database_name": "notes", "database_id": "…" }
]
}
src/index.ts
import { Hono } from "hono";
const app = new Hono<{ Bindings: Env }>();
app.get("/api/notes", async (c) => {
const { results } = await c.env.DB
.prepare("SELECT id, title FROM notes ORDER BY id DESC")
.all();
return c.json(results);
});
// The Worker only gets /api/*; Static Assets serves the rest without running it.
// An API route that does not exist responds 404 as JSON.
app.notFound((c) => c.json({ error: "Not found" }, 404));
export default app;

Así decide Cloudflare quién atiende cada petición con esta configuración:

flowchart TD
    P[Petición] --> R{¿Empieza por /api/?}
    R -->|Sí| W[Tu Worker<br/>se cobra]
    R -->|No| A{¿Existe el archivo?}
    A -->|Sí| F[Static Assets lo sirve<br/>gratis]
    A -->|No| I[index.html<br/>modo SPA]
    W --> N{¿Existe la ruta?}
    N -->|No| E[404 en JSON]

Opción 3: full-stack con un framework

Los frameworks con renderizado en servidor se integran con el plugin de Vite de Cloudflare (@cloudflare/vite-plugin): el servidor de desarrollo de Vite ejecuta tu código en workerd, con los mismos bindings que producción.

Framework Cómo se integra Nota
Astro Estático por defecto; para SSR, el adaptador de Cloudflare Mi opción para webs de contenido
React Router v7 Plantilla de Cloudflare con el plugin de Vite Buena para aplicaciones con muchas pantallas
Next.js vinext (recomendado por Cloudflare para proyectos nuevos) u OpenNext Comprueba qué funciones de Next.js necesitas antes de elegir
SvelteKit, Nuxt, Hono + JSX Adaptadores o plantillas de C3 npm create cloudflare@latest te ofrece la plantilla
Terminal
npm create cloudflare@latest my-app -- --framework=react-router

Cachear respuestas dinámicas con Workers Cache

Si una ruta dinámica devuelve lo mismo durante un rato (un listado, una página de producto), Workers Cache la sirve sin ejecutar tu código:

wrangler.jsonc (extracto)
{
"cache": { "enabled": true }
}
src/index.ts (extracto)
app.get("/api/recent", async (c) => {
const { results } = await c.env.DB
.prepare("SELECT id, title FROM notes ORDER BY created_at DESC LIMIT 10")
.all();
return c.json(results, 200, {
"Cache-Control": "public, max-age=300, stale-while-revalidate=60",
"Cache-Tag": "recent",
});
});

Para borrar esa caché al publicar algo nuevo, importa cache de cloudflare:workers (el c.executionCtx de Hono no lo tipa) y llama a await cache.purge({ tags: ["recent"] }). En local no se simula: la purga solo funciona desplegado.

Desplegar

Terminal
npm run build
npx wrangler deploy

Wrangler sube solo los archivos que han cambiado desde el último despliegue.

Cuánto cuesta

Cuánto cuesta al mes

Supuestos: Una SPA con 100.000 visitas al mes (600.000 peticiones a archivos) y 300.000 llamadas a la API

ConceptoImporte
Peticiones a Static AssetsGratis e ilimitadas0,00 $
Peticiones a la APIUnas 10.000 al día: caben en el plan gratuito0,00 $
Almacenamiento de los archivosSin coste adicional0,00 $
Total al mes0,00 $

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

Límites de Static Assets

Límite Plan gratuito Plan de pago
Archivos por versión 20.000 Más (consultar el límite vigente)
Tamaño máximo por archivo 25 MiB 25 MiB

Para archivos más grandes (vídeos, descargas), usa R2.

Resumen

  • Static Assets sirve tu frontend gratis; el Worker solo se ejecuta donde lo necesitas.
  • SPA + API en un Worker con not_found_handling: "single-page-application" y run_worker_first: ["/api/*"].
  • Workers Cache para respuestas dinámicas, sabiendo que cambia la facturación de los estáticos.
  • Siguiente capítulo: bases de datos.

Recursos

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