Deploy estático: Pages, Netlify, GitHub Pages y S3
Cómo publicar un dist estático en las plataformas Git-driven como Cloudflare Pages, Netlify o GitHub Pages, y en un cubo S3 con CDN. El comando de build, la carpeta de salida, y cómo alinear site, base y trailingSlash con las reglas de rutas y barra final del host para evitar enlaces rotos.
Un dist/ estático es la forma de despliegue más benigna que existe: no es un programa que arranca, es una carpeta de ficheros terminados. Publicarlo es, en esencia, copiar esos bytes a un servidor de borde y apuntar un dominio hacia ellos. Por eso el estático se despliega en casi cualquier sitio —Cloudflare Pages, Netlify, GitHub Pages, un cubo S3 tras un CDN— y por eso es tan barato y tan difícil de tumbar: no hay proceso que se caiga, solo ficheros que se sirven. La única complejidad real no está en subir la carpeta, sino en que la forma de tus URLs coincida con lo que el host espera. Ahí es donde site, base y trailingSlash deciden si el despliegue sale limpio o se llena de enlaces rotos.
- Entender por qué desplegar estático es copiar una carpeta a un borde.
- Publicar en plataformas Git-driven: Cloudflare Pages, Netlify y GitHub Pages.
- Subir un
dist/a un cubo S3 servido por un CDN y controlar el caché. - Alinear
site,baseytrailingSlashcon las reglas de rutas del host.
Desplegar estático es copiar una carpeta
Todo despliegue estático se reduce a dos datos: qué comando construye el sitio y qué carpeta contiene el resultado. Para Astro, esos dos datos son casi siempre los mismos, y cualquier plataforma te pedirá justo eso.
# Comando de build: astro build (o npm run build)
# Directorio de salida: dist
A partir de ahí, las plataformas se dividen en dos filosofías. Las Git-driven —Pages, Netlify, GitHub Pages— observan tu repositorio, ejecutan el build por ti cuando detectan un commit y publican dist/ en su red. Las de infraestructura —S3 con un CDN delante— esperan que tú construyas y subas la carpeta, dándote a cambio control total sobre el almacenamiento y las reglas de caché. La primera vía cambia código por conveniencia; la segunda, conveniencia por control. Ninguna es superior: dependen de cuánta infraestructura quieras poseer.
El reparto de responsabilidades entre tú y la plataforma resume la elección mejor que cualquier tabla de precios:
- Quién construye: la nube en Git-driven; tú en local antes de subir a S3.
- Quién guarda el historial: el proveedor, un despliegue por commit; o tú, por versiones del cubo.
- Quién controla el caché: la plataforma con valores por defecto; o tú, regla a regla en el CDN.
- Qué cuesta empezar: casi nada en Git-driven; montar y mantener la infraestructura en S3.
Cloudflare Pages
Conecta el repositorio, fija el comando astro build y la salida dist. Publica en la red de borde global sin configurar nada más.
Netlify
Detecta Astro casi solo. Previews por rama, redirecciones y cabeceras declaradas en fichero, todo integrado con el repositorio.
GitHub Pages
Gratis para proyectos abiertos. Suele servir bajo el nombre del repositorio, así que casi siempre exige configurar base.
S3 mas CDN
Un cubo de objetos tras una distribución de CDN. Máximo control de caché, invalidaciones y rutas, a cambio de montarlo tú.
Plataformas Git-driven: el build vive en la nube
En las plataformas Git-driven no subes dist/ a mano: subes tu código, y ellas ejecutan el build en sus propios servidores. El contrato es mínimo —comando de build y carpeta de salida— y a cambio obtienes despliegues automáticos en cada push y, casi siempre, una URL de vista previa por cada rama o pull request.
# Lo que la plataforma ejecuta por ti, en su infraestructura:
npm ci # instala dependencias de forma reproducible
npm run build # produce dist/
# y publica dist/ en su red de borde
Netlify y Cloudflare Pages detectan un proyecto Astro casi sin ayuda, y permiten declarar redirecciones, cabeceras y reglas en un fichero versionado, de modo que la configuración del host viaja con el repositorio. GitHub Pages es el caso más peculiar: sirve el sitio bajo una ruta que incluye el nombre del repositorio —algo como usuario.github.io/mi-repo— y esa subruta es exactamente el problema que resuelve base, como veremos. La ventaja transversal de este modelo es la trazabilidad: cada versión desplegada corresponde a un commit, y volver atrás es redeploy de un commit anterior.
Esa trazabilidad tiene un efecto más cultural que técnico: cuando cada despliegue es un commit y cada commit una URL, el equipo deja de temer al botón de publicar. Un cambio arriesgado se prueba en su preview, se revisa en vivo y, si sale mal, se revierte volviendo al commit anterior. La plataforma no solo despliega: convierte tu historial de git en tu historial de producción, con la misma capacidad de auditar y deshacer.
En tu máquina usas npm install, pero en el servidor de despliegue conviene npm ci: instala exactamente las versiones fijadas en el lockfile, sin actualizarlo ni resolver rangos. Eso garantiza que el build de la nube use las mismas dependencias que probaste en local, y elimina la clase de fallo más frustrante —el que solo aparece en el servidor porque allí se instaló una versión distinta de un paquete—. Un despliegue fiable empieza por un árbol de dependencias idéntico en todas partes.
S3 y CDN: subir y controlar el caché
Cuando quieres poseer la infraestructura, el patrón clásico es un cubo de objetos —S3 o cualquier compatible— servido por un CDN que lo cachea en el borde. Aquí construyes tú y subes la carpeta, normalmente con una herramienta de sincronización.
astro build
# sincroniza dist/ con el cubo, borrando lo que ya no existe
aws s3 sync dist/ s3://mi-sitio --delete
# invalida el cache del CDN para servir la version nueva
aws cloudfront create-invalidation --paths "/*"
Dos detalles separan un despliegue estático amateur de uno serio. El primero es la invalidación: un CDN cachea agresivamente, así que tras subir la nueva versión debes decirle que olvide la vieja, o los visitantes seguirán viendo la anterior. El segundo es la estrategia de caché diferenciada: los ficheros de _astro llevan un hash en el nombre y pueden cachearse para siempre —si cambian, cambia el nombre—, mientras que los HTML deben revalidarse a menudo, porque su nombre no cambia aunque su contenido sí. Configurar esas dos políticas de Cache-Control distintas es lo que hace que el sitio sea rápido sin quedarse obsoleto.
En la práctica, tres reglas de caché cubren casi cualquier sitio estático:
- Para
_astroy todo asset con hash en el nombre: una caché inmutable y de vida muy larga, sin miedo. - Para los HTML y cada
index.html: caché corta o revalidación, porque el nombre no cambia con el contenido. - Para
public: depende del fichero —larga para imágenes estables, corta para unrobots.txtque edites a menudo—.
La razón de que puedas cachear _astro para siempre no es un acto de fe: es que el nombre del fichero deriva de su contenido. Si el CSS cambia una coma, cambia su hash y, con él, su nombre; el navegador pide un fichero nuevo y jamás sirve el viejo. Esa es la virtud del cache busting por contenido: no necesitas invalidar nada, porque una versión distinta es, literalmente, un fichero distinto. El único recurso que sí debes revalidar es el HTML, que conserva su nombre aunque su interior cambie.
site, base y trailingSlash: alinear con el host
La parte que de verdad rompe despliegues estáticos no es subir la carpeta, sino la forma de las URLs. Tres claves de configuración deben coincidir con lo que el host hace, o aparecerán enlaces rotos y redirecciones en bucle.
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://usuario.github.io',
base: '/mi-repo',
trailingSlash: 'always',
});
site declara el dominio canónico y alimenta el sitemap, las etiquetas canónicas y los feeds. base es imprescindible cuando el host sirve bajo un subdirectorio —el caso de GitHub Pages con el nombre del repositorio—; sin él, todos los enlaces absolutos apuntarán fuera de la subruta y el sitio se romperá entero. Y trailingSlash debe pactar con la política del host: muchos CDNs redirigen por su cuenta entre /blog y /blog/, y si tu configuración contradice la suya, el navegador entra en redirecciones dobles. La regla práctica: elige una política de barra final, decláralar en Astro y verifica que el host respeta la misma.
Hay una asimetría que conviene interiorizar para no perseguir el error equivocado. site afecta a lo que el mundo exterior ve de ti —las URLs absolutas de tu sitemap y tus canónicas—, mientras que base y trailingSlash gobiernan cómo se resuelven las rutas dentro del propio sitio. Confundir esos dos planos es la causa de la mitad de los despliegues que cargan pero enlazan mal: tocas uno esperando el efecto del otro y el síntoma no se mueve.
El síntoma es inconfundible: la página carga sin estilos, sin imágenes y con todos los enlaces muertos. La causa casi siempre es un base ausente o incorrecto cuando el sitio vive en un subdirectorio. Recuerda que base no reescribe tus href escritos a mano —debes componer los enlaces internos con import.meta.env.BASE_URL—, y que los assets de public también necesitan ese prefijo. Antes de dar por bueno un despliegue en subruta, comprueba en astro preview que ningún recurso apunta a la raíz del dominio.
flowchart LR PUSH[git push a main] --> HOOK[la plataforma detecta el commit] HOOK --> INS[npm ci instala dependencias] INS --> BLD[astro build produce dist] BLD --> PUB[publica dist en el CDN global] PUB --> URL[URL en produccion] style BLD fill:#89b4fa,color:#11111b style URL fill:#a6e3a1,color:#11111b
Hay una tentación, cuando uno domina el despliegue dinámico, de mirar el sitio estático como el hermano menor —lo simple, lo que se supera—. Es un error de perspectiva. El despliegue estático no es una limitación que toleras: es una propiedad que compras. Un dist/ de ficheros planos no tiene proceso que se caiga, ni base de datos que saturar, ni runtime que actualizar, ni superficie de ataque que parchear. Se replica en cien puntos de presencia sin coordinación, se sirve con latencia de milisegundos desde el borde más cercano al visitante, y sobrevive a picos de tráfico que tumbarían a un servidor, porque servir un fichero cacheado es la operación más barata de internet. Toda esa robustez no viene de una tecnología sofisticada, sino de una ausencia: no hay nada que ejecutar en el momento de la petición. Y esa ausencia es precisamente lo que hace triviales las tres plataformas de esta lección —Pages, Netlify, Pages de GitHub, S3— porque todas hacen lo mismo por debajo, copiar bytes a un borde, y solo difieren en cuánta infraestructura te ocultan. La disciplina que sí exige el estático no está en desplegarlo, sino en pensarlo: decidir qué es contenido y puede hornearse, alinear la forma de las URLs con el host, distinguir lo que se cachea para siempre de lo que debe revalidarse. Domina eso y tendrás la clase de sitio que sigue en pie el día que todos los demás se caen: el que no depende de que nada esté funcionando para funcionar.
- Conecta un repositorio a una plataforma Git-driven, fija
astro buildy salidadist, y observa el primer despliegue automático. - Configura
basepara un despliegue en subdirectorio y verifica enastro previewque ningún asset apunta a la raíz. - Construye en local y sube
dist/a un cubo con una orden de sincronización; invalida después el caché del CDN. - Cambia
trailingSlashy comprueba cómo reacciona el host: busca alguna redirección doble y razona cómo evitarla.