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.
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: distA 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.
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 reproduciblenpm run build # produce dist/# y publica dist/ en su red de bordeNetlify 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.
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 existeaws s3 sync dist/ s3://mi-sitio --delete# invalida el cache del CDN para servir la version nuevaaws 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—.
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.mjsimport { 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.