Problema identificado: Las URLs de subdirectorios (/meetups/, /comunidad/, etc.) devuelven error 404, mientras que la página principal funciona correctamente.
Causa raíz:
- MkDocs genera el sitio con
--use-directory-urls, creando URLs limpias como/meetups/→site/meetups/index.html - CloudFront tiene configurado
default_root_object = "index.html"que solo funciona para la raíz (/) - Para subdirectorios, CloudFront busca un objeto llamado
meetups/en S3 (sinindex.html) - Como ese objeto no existe, S3 devuelve 403, que CloudFront convierte en 404
Se implementó CloudFront Functions para reescribir automáticamente las URLs y agregar index.html a las rutas que terminan en / o no tienen extensión.
- Crea dos CloudFront Functions (producción y staging)
- Función JavaScript que intercepta requests y añade
index.htmlautomáticamente - Maneja dos casos:
- URLs que terminan en
/→ Añadeindex.html - URLs sin extensión de archivo → Añade
/index.html
- URLs que terminan en
- Líneas 46-50: Asocia la función CloudFront al
default_cache_behavior - La función se ejecuta en el evento
viewer-request(antes de llegar a S3)
- Líneas 46-50: Asocia la función CloudFront de staging al
default_cache_behavior - Misma lógica aplicada al ambiente de staging
- Acceso a AWS con credenciales configuradas
- Terraform instalado
- Variables de Terraform configuradas (archivo
terraform.tfvars)
-
Navega al directorio de Terraform:
cd terraform -
Revisa el plan de Terraform:
terraform plan
Deberías ver:
+ aws_cloudfront_function.directory_index(nuevo)+ aws_cloudfront_function.directory_index_staging(nuevo)~ aws_cloudfront_distribution.website(modificado)~ aws_cloudfront_distribution.website_staging(modificado)
-
Aplica los cambios:
terraform apply
-
Confirma los cambios: Escribe
yescuando se te solicite
- CloudFront Functions: Se despliegan inmediatamente en todas las edge locations
- Distribución de CloudFront: Puede tardar 5-15 minutos en propagarse completamente
- Cache: Si hay contenido en caché, puede tardar hasta 1 hora (basado en
max_ttl)
Para aplicar los cambios inmediatamente sin esperar la expiración del caché:
# Para producción
aws cloudfront create-invalidation \
--distribution-id <DISTRIBUTION_ID> \
--paths "/*"
# Para staging
aws cloudfront create-invalidation \
--distribution-id <STAGING_DISTRIBUTION_ID> \
--paths "/*"Puedes obtener los Distribution IDs con:
terraform output cloudfront_distribution_id
terraform output cloudfront_staging_distribution_idUna vez desplegado, verifica que las siguientes URLs funcionan:
- ✅
https://pythoncdmx.org/(ya funcionaba) - ✅
https://pythoncdmx.org/meetups/ - ✅
https://pythoncdmx.org/meetups/2025/ - ✅
https://pythoncdmx.org/comunidad/ - ✅
https://pythoncdmx.org/comunidad/ponentes/ - ✅
https://pythoncdmx.org/comunidad/voluntarios/ - ✅
https://pythoncdmx.org/blog/
- ✅ Todas las rutas equivalentes en el dominio de staging
- ✅ URLs limpias: Mantiene
/meetups/en lugar de/meetups.html - ✅ SEO amigable: Las URLs siguen siendo las mismas
- ✅ Sin cambios en el código: No requiere modificar MkDocs
- ✅ Bajo costo: CloudFront Functions es prácticamente gratis ($0.10 por millón de invocaciones)
- ✅ Alta performance: Se ejecuta en edge locations (latencia mínima)
- ✅ Escalable: Funciona automáticamente para cualquier nueva página
- CloudFront Functions: ~$0.10 por millón de requests
- Para un sitio con 100,000 visitas/mes: ~$0.01/mes
Si después del despliegue aún hay errores 404:
-
Verifica que la función esté asociada:
aws cloudfront get-distribution --id <DISTRIBUTION_ID> \ | jq '.Distribution.DistributionConfig.DefaultCacheBehavior.FunctionAssociations'
-
Verifica que la función esté publicada:
aws cloudfront list-functions
-
Revisa CloudWatch Logs (si está habilitado):
aws logs tail /aws/cloudfront/function/pythoncdmx-directory-index --follow
-
Invalida el caché de CloudFront (ver comando arriba)
-
Prueba con curl para ver headers:
curl -I https://pythoncdmx.org/meetups/
function handler(event) {
var request = event.request;
var uri = request.uri;
// Ejemplo: /meetups/ → /meetups/index.html
if (uri.endsWith('/')) {
request.uri += 'index.html';
}
// Ejemplo: /meetups → /meetups/index.html
else if (!uri.includes('.')) {
request.uri += '/index.html';
}
return request;
}Flujo de ejecución:
- Usuario solicita
https://pythoncdmx.org/meetups/ - CloudFront recibe el request en la edge location
- CloudFront Function intercepta y reescribe:
/meetups/→/meetups/index.html - CloudFront solicita a S3:
s3://bucket/meetups/index.html - S3 devuelve el archivo (existe en S3 gracias a MkDocs)
- CloudFront devuelve la respuesta al usuario
-
Lambda@Edge: Más potente pero:
- ❌ Más costoso (~$0.60 por millón vs $0.10)
- ❌ Mayor latencia (ejecuta en regional edge cache)
- ❌ Más complejo de mantener
-
Cambiar a
--no-directory-urls:- ❌ URLs menos amigables (
/meetups.html) - ❌ Rompe links existentes
- ❌ Peor SEO
- ❌ URLs menos amigables (
-
S3 Redirects:
- ❌ No funciona con CloudFront OAC
- ❌ Requiere S3 public (inseguro)
- Desplegar los cambios siguiendo la sección "Pasos para Desplegar"
- Verificar que todas las URLs funcionan correctamente
- Monitorear CloudFront metrics durante las primeras 24 horas
- Documentar en el README del proyecto que se usa CloudFront Functions
Si encuentras problemas durante el despliegue:
- Revisa el output de
terraform planyterraform apply - Verifica los logs de CloudWatch (si están habilitados)
- Consulta la documentación de AWS:
Fecha de implementación: 2025-10-25 Autor: Claude Code Versión: 1.0