Del repositorio a una URL pública#

El frontend es la parte que ven tus usuarios: componentes, estilos y la lógica de interacción. Desplegarlo bien significa que cada commit en la rama correcta termine publicado en la URL correcta, de forma repetible y sin que nadie copie archivos a mano.

Toda plataforma de despliegue de frontends, sea cual sea, te pide más o menos lo mismo: el repositorio, un nombre DNS, cómo compilar el proyecto, si el contenido es estático o se renderiza en el servidor, y una ruta que responda para comprobar que el sitio está vivo. Esta guía muestra cómo resolver esas mismas piezas directamente con servicios de AWS.

La primera decisión es la más importante: ¿tu build produce archivos estáticos o necesita un servidor en cada petición?

Estático o SSR: elige la arquitectura#

Si el comando de build genera una carpeta de HTML, CSS y JavaScript que el navegador carga tal cual (una SPA con Vite o una exportación estática de Next.js), tu sitio es estático. Amazon S3 puede almacenarlo, pero no ejecuta código del lado del servidor. Si cada petición necesita un proceso Node.js que renderice HTML, necesitas cómputo.

OpciónCuándo encajaQué gestionas tú
S3 privado + CloudFront (OAC)Sitios estáticos y SPAsBucket, distribución, política, pipeline de CI
AWS Amplify HostingEstáticos, SPAs y frameworks con SSR; flujo basado en GitConfiguración de build y ramas; AWS gestiona el resto
Contenedor en ECS (por ejemplo ECS Express Mode)SSR con requisitos propios de runtime o redImagen, servicio, balanceador y escalado

Esta guía detalla la primera opción, que es la más común para SPAs, y resume Amplify. El caso de contenedor se cubre en la guía para desplegar un servicio HTTP en contenedores en AWS.

Documentación: Amazon S3 · Hosting de sitios estáticos ↗ · Vite · Despliegue estático ↗ · Next.js · Static exports ↗ · Amazon ECS · Express Mode ↗

S3 privado con CloudFront y OAC#

El patrón recomendado es un bucket de S3 sin acceso público (con Block Public Access activado) y una distribución de CloudFront delante que lee del bucket mediante origin access control (OAC). AWS recomienda OAC; origin access identity (OAI) es el mecanismo anterior y figura como legacy. La política del bucket solo permite leer a la distribución concreta:

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontServicePrincipalReadOnly",
      "Effect": "Allow",
      "Principal": { "Service": "cloudfront.amazonaws.com" },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/EDFDVBD6EXAMPLE"
        }
      }
    }
  ]
}
Política de bucket basada en el ejemplo de la documentación de CloudFront. Sustituye el nombre del bucket, el ID de cuenta y el ID de la distribución.
  • Configura index.html como default root object de la distribución. Ojo: solo aplica a la raíz, no a subdirectorios.
  • Para una SPA con rutas del lado del cliente, añade respuestas de error personalizadas que devuelvan /index.html con código 200 ante 403 y 404. Con OAC y sin permiso s3:ListBucket, S3 responde 403 (no 404) cuando el objeto no existe.
  • Usa tu dominio con un certificado de ACM y un alias en Route 53 apuntando a la distribución.

Documentación: CloudFront · Restringir acceso a S3 (OAC) ↗ · Amazon S3 · Block Public Access ↗ · CloudFront · Default root object ↗ · CloudFront · Cambiar códigos de respuesta ↗ · Amazon S3 · GetObject y 403 ↗

Pipeline con GitHub Actions y OIDC#

El pipeline compila en cada push a main, obtiene credenciales temporales de AWS mediante OIDC (sin access keys guardadas en GitHub), sube los archivos y invalida lo que no debe quedar en caché:

yaml
# .github/workflows/deploy-frontend.yml
name: deploy-frontend
on:
  push:
    branches: [main]

permissions:
  id-token: write   # necesario para pedir el token OIDC
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: prod
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm run build            # genera dist/

      - uses: aws-actions/configure-aws-credentials@v6
        with:
          role-to-assume: arn:aws:iam::111122223333:role/frontend-deploy
          aws-region: us-east-1

      # Assets con hash en el nombre: caché larga
      - run: >
          aws s3 sync dist/ s3://amzn-s3-demo-bucket/ --delete
          --exclude index.html
          --cache-control "public,max-age=31536000,immutable"
      # index.html: siempre revalidar
      - run: >
          aws s3 cp dist/index.html s3://amzn-s3-demo-bucket/index.html
          --cache-control "no-cache"
      - run: >
          aws cloudfront create-invalidation
          --distribution-id EDFDVBD6EXAMPLE --paths "/index.html"
El rol frontend-deploy debe confiar en el proveedor OIDC de GitHub y limitar el sub al repositorio y environment; sus permisos, solo s3 sync al bucket y create-invalidation en la distribución.

La estrategia de caché evita invalidaciones masivas. Los bundlers generan nombres con hash, así que esos archivos pueden cachearse un año; index.html se sirve con no-cache y es lo único que se invalida. AWS documenta esta alternativa como usar nombres de archivo versionados en lugar de invalidar.

Documentación: GitHub · OIDC con AWS ↗ · configure-aws-credentials ↗ · AWS CLI · s3 sync ↗ · AWS CLI · create-invalidation ↗ · CloudFront · Invalidación vs nombres versionados ↗ · CloudFront · Expiración de contenido ↗

Alternativa gestionada: Amplify Hosting#

Amplify Hosting ofrece un flujo basado en Git con despliegue continuo: conectas el repositorio, Amplify compila en cada push y publica en su CDN. Soporta frameworks con SSR, SPAs y generadores de sitios estáticos.

Cada rama conectada se convierte en un despliegue propio con su URL (por ejemplo main y dev), lo que encaja con tener un ambiente por rama. Es la opción con menos piezas cuando necesitas SSR y no quieres operar contenedores; a cambio, tienes menos control sobre la infraestructura que con S3 y CloudFront.

Documentación: AWS Amplify Hosting ↗ · Amplify · Despliegues por rama ↗

Verificar el despliegue#

  • Abre la URL pública y navega directamente a una ruta interna (por ejemplo /perfil) y recarga: si ves la app y no un error XML de S3, las respuestas de error están bien configuradas.
  • Comprueba que el bucket no responde si accedes a su endpoint directo; solo debe servir a través de CloudFront.
  • Revisa las cabeceras Cache-Control de index.html y de un asset con hash.
  • Define una ruta de salud simple (la raíz suele bastar) que devuelva 200 y úsala en tu monitorización.

Checklist#

  • Build reproducible (npm ci, versión de Node fijada).
  • Bucket privado, OAC y política limitada a la distribución.
  • Respuestas 403/404 a /index.html solo si es una SPA.
  • Credenciales de CI por OIDC con permisos mínimos; nada de secretos en el bundle del cliente.
  • Caché larga para assets con hash; no-cache para index.html.

Fuentes y alcance

Documentación consultada el 25 de septiembre de 2026. Los ejemplos y criterios de decisión son propuestas editoriales; adáptalos al contrato de tu aplicación y valídalos en tu entorno de pruebas autorizado.

Del diseño a la decisión

Compara soluciones cloud

Revisa tarifas, límites, condiciones y fuentes de cada opción.

Abrir comparador