|
| 1 | +# Solución a Errores 404 en pythoncdmx.org |
| 2 | + |
| 3 | +## 📋 Resumen del Problema |
| 4 | + |
| 5 | +**Problema identificado:** Las URLs de subdirectorios (`/meetups/`, `/comunidad/`, etc.) devuelven error 404, mientras que la página principal funciona correctamente. |
| 6 | + |
| 7 | +**Causa raíz:** |
| 8 | +- MkDocs genera el sitio con `--use-directory-urls`, creando URLs limpias como `/meetups/` → `site/meetups/index.html` |
| 9 | +- CloudFront tiene configurado `default_root_object = "index.html"` que **solo funciona para la raíz** (`/`) |
| 10 | +- Para subdirectorios, CloudFront busca un objeto llamado `meetups/` en S3 (sin `index.html`) |
| 11 | +- Como ese objeto no existe, S3 devuelve 403, que CloudFront convierte en 404 |
| 12 | + |
| 13 | +## ✅ Solución Implementada |
| 14 | + |
| 15 | +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. |
| 16 | + |
| 17 | +### Archivos Modificados/Creados |
| 18 | + |
| 19 | +#### 1. **Nuevo:** `terraform/cloudfront-function.tf` |
| 20 | +- Crea dos CloudFront Functions (producción y staging) |
| 21 | +- Función JavaScript que intercepta requests y añade `index.html` automáticamente |
| 22 | +- Maneja dos casos: |
| 23 | + - URLs que terminan en `/` → Añade `index.html` |
| 24 | + - URLs sin extensión de archivo → Añade `/index.html` |
| 25 | + |
| 26 | +#### 2. **Modificado:** `terraform/cloudfront.tf` |
| 27 | +- Líneas 46-50: Asocia la función CloudFront al `default_cache_behavior` |
| 28 | +- La función se ejecuta en el evento `viewer-request` (antes de llegar a S3) |
| 29 | + |
| 30 | +#### 3. **Modificado:** `terraform/cloudfront-staging.tf` |
| 31 | +- Líneas 46-50: Asocia la función CloudFront de staging al `default_cache_behavior` |
| 32 | +- Misma lógica aplicada al ambiente de staging |
| 33 | + |
| 34 | +## 🚀 Pasos para Desplegar |
| 35 | + |
| 36 | +### Prerequisitos |
| 37 | +- Acceso a AWS con credenciales configuradas |
| 38 | +- Terraform instalado |
| 39 | +- Variables de Terraform configuradas (archivo `terraform.tfvars`) |
| 40 | + |
| 41 | +### Despliegue |
| 42 | + |
| 43 | +1. **Navega al directorio de Terraform:** |
| 44 | + ```bash |
| 45 | + cd terraform |
| 46 | + ``` |
| 47 | + |
| 48 | +2. **Revisa el plan de Terraform:** |
| 49 | + ```bash |
| 50 | + terraform plan |
| 51 | + ``` |
| 52 | + |
| 53 | + Deberías ver: |
| 54 | + - `+ aws_cloudfront_function.directory_index` (nuevo) |
| 55 | + - `+ aws_cloudfront_function.directory_index_staging` (nuevo) |
| 56 | + - `~ aws_cloudfront_distribution.website` (modificado) |
| 57 | + - `~ aws_cloudfront_distribution.website_staging` (modificado) |
| 58 | + |
| 59 | +3. **Aplica los cambios:** |
| 60 | + ```bash |
| 61 | + terraform apply |
| 62 | + ``` |
| 63 | + |
| 64 | +4. **Confirma los cambios:** Escribe `yes` cuando se te solicite |
| 65 | + |
| 66 | +### Tiempo de Propagación |
| 67 | + |
| 68 | +- **CloudFront Functions:** Se despliegan inmediatamente en todas las edge locations |
| 69 | +- **Distribución de CloudFront:** Puede tardar 5-15 minutos en propagarse completamente |
| 70 | +- **Cache:** Si hay contenido en caché, puede tardar hasta 1 hora (basado en `max_ttl`) |
| 71 | + |
| 72 | +### Invalidación de Caché (Opcional pero Recomendado) |
| 73 | + |
| 74 | +Para aplicar los cambios inmediatamente sin esperar la expiración del caché: |
| 75 | + |
| 76 | +```bash |
| 77 | +# Para producción |
| 78 | +aws cloudfront create-invalidation \ |
| 79 | + --distribution-id <DISTRIBUTION_ID> \ |
| 80 | + --paths "/*" |
| 81 | + |
| 82 | +# Para staging |
| 83 | +aws cloudfront create-invalidation \ |
| 84 | + --distribution-id <STAGING_DISTRIBUTION_ID> \ |
| 85 | + --paths "/*" |
| 86 | +``` |
| 87 | + |
| 88 | +Puedes obtener los Distribution IDs con: |
| 89 | +```bash |
| 90 | +terraform output cloudfront_distribution_id |
| 91 | +terraform output cloudfront_staging_distribution_id |
| 92 | +``` |
| 93 | + |
| 94 | +## 🧪 Verificación |
| 95 | + |
| 96 | +Una vez desplegado, verifica que las siguientes URLs funcionan: |
| 97 | + |
| 98 | +### Producción (pythoncdmx.org) |
| 99 | +- ✅ `https://pythoncdmx.org/` (ya funcionaba) |
| 100 | +- ✅ `https://pythoncdmx.org/meetups/` |
| 101 | +- ✅ `https://pythoncdmx.org/meetups/2025/` |
| 102 | +- ✅ `https://pythoncdmx.org/comunidad/` |
| 103 | +- ✅ `https://pythoncdmx.org/comunidad/ponentes/` |
| 104 | +- ✅ `https://pythoncdmx.org/comunidad/voluntarios/` |
| 105 | +- ✅ `https://pythoncdmx.org/blog/` |
| 106 | + |
| 107 | +### Staging (si aplica) |
| 108 | +- ✅ Todas las rutas equivalentes en el dominio de staging |
| 109 | + |
| 110 | +## 📊 Impacto y Beneficios |
| 111 | + |
| 112 | +### Ventajas de la Solución |
| 113 | +- ✅ **URLs limpias:** Mantiene `/meetups/` en lugar de `/meetups.html` |
| 114 | +- ✅ **SEO amigable:** Las URLs siguen siendo las mismas |
| 115 | +- ✅ **Sin cambios en el código:** No requiere modificar MkDocs |
| 116 | +- ✅ **Bajo costo:** CloudFront Functions es prácticamente gratis ($0.10 por millón de invocaciones) |
| 117 | +- ✅ **Alta performance:** Se ejecuta en edge locations (latencia mínima) |
| 118 | +- ✅ **Escalable:** Funciona automáticamente para cualquier nueva página |
| 119 | + |
| 120 | +### Costo Estimado |
| 121 | +- **CloudFront Functions:** ~$0.10 por millón de requests |
| 122 | +- Para un sitio con 100,000 visitas/mes: **~$0.01/mes** |
| 123 | + |
| 124 | +## 🔍 Debugging |
| 125 | + |
| 126 | +Si después del despliegue aún hay errores 404: |
| 127 | + |
| 128 | +1. **Verifica que la función esté asociada:** |
| 129 | + ```bash |
| 130 | + aws cloudfront get-distribution --id <DISTRIBUTION_ID> \ |
| 131 | + | jq '.Distribution.DistributionConfig.DefaultCacheBehavior.FunctionAssociations' |
| 132 | + ``` |
| 133 | + |
| 134 | +2. **Verifica que la función esté publicada:** |
| 135 | + ```bash |
| 136 | + aws cloudfront list-functions |
| 137 | + ``` |
| 138 | + |
| 139 | +3. **Revisa CloudWatch Logs (si está habilitado):** |
| 140 | + ```bash |
| 141 | + aws logs tail /aws/cloudfront/function/pythoncdmx-directory-index --follow |
| 142 | + ``` |
| 143 | + |
| 144 | +4. **Invalida el caché de CloudFront** (ver comando arriba) |
| 145 | + |
| 146 | +5. **Prueba con curl para ver headers:** |
| 147 | + ```bash |
| 148 | + curl -I https://pythoncdmx.org/meetups/ |
| 149 | + ``` |
| 150 | + |
| 151 | +## 📝 Notas Técnicas |
| 152 | + |
| 153 | +### Cómo Funciona la CloudFront Function |
| 154 | + |
| 155 | +```javascript |
| 156 | +function handler(event) { |
| 157 | + var request = event.request; |
| 158 | + var uri = request.uri; |
| 159 | + |
| 160 | + // Ejemplo: /meetups/ → /meetups/index.html |
| 161 | + if (uri.endsWith('/')) { |
| 162 | + request.uri += 'index.html'; |
| 163 | + } |
| 164 | + // Ejemplo: /meetups → /meetups/index.html |
| 165 | + else if (!uri.includes('.')) { |
| 166 | + request.uri += '/index.html'; |
| 167 | + } |
| 168 | + |
| 169 | + return request; |
| 170 | +} |
| 171 | +``` |
| 172 | + |
| 173 | +**Flujo de ejecución:** |
| 174 | +1. Usuario solicita `https://pythoncdmx.org/meetups/` |
| 175 | +2. CloudFront recibe el request en la edge location |
| 176 | +3. **CloudFront Function** intercepta y reescribe: `/meetups/` → `/meetups/index.html` |
| 177 | +4. CloudFront solicita a S3: `s3://bucket/meetups/index.html` |
| 178 | +5. S3 devuelve el archivo (existe en S3 gracias a MkDocs) |
| 179 | +6. CloudFront devuelve la respuesta al usuario |
| 180 | + |
| 181 | +### Alternativas Consideradas (No Implementadas) |
| 182 | + |
| 183 | +1. **Lambda@Edge:** Más potente pero: |
| 184 | + - ❌ Más costoso (~$0.60 por millón vs $0.10) |
| 185 | + - ❌ Mayor latencia (ejecuta en regional edge cache) |
| 186 | + - ❌ Más complejo de mantener |
| 187 | + |
| 188 | +2. **Cambiar a `--no-directory-urls`:** |
| 189 | + - ❌ URLs menos amigables (`/meetups.html`) |
| 190 | + - ❌ Rompe links existentes |
| 191 | + - ❌ Peor SEO |
| 192 | + |
| 193 | +3. **S3 Redirects:** |
| 194 | + - ❌ No funciona con CloudFront OAC |
| 195 | + - ❌ Requiere S3 public (inseguro) |
| 196 | + |
| 197 | +## 🎯 Próximos Pasos |
| 198 | + |
| 199 | +1. **Desplegar los cambios** siguiendo la sección "Pasos para Desplegar" |
| 200 | +2. **Verificar** que todas las URLs funcionan correctamente |
| 201 | +3. **Monitorear** CloudFront metrics durante las primeras 24 horas |
| 202 | +4. **Documentar** en el README del proyecto que se usa CloudFront Functions |
| 203 | + |
| 204 | +## 🆘 Soporte |
| 205 | + |
| 206 | +Si encuentras problemas durante el despliegue: |
| 207 | + |
| 208 | +1. Revisa el output de `terraform plan` y `terraform apply` |
| 209 | +2. Verifica los logs de CloudWatch (si están habilitados) |
| 210 | +3. Consulta la documentación de AWS: |
| 211 | + - [CloudFront Functions](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html) |
| 212 | + - [CloudFront Distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html) |
| 213 | + |
| 214 | +--- |
| 215 | + |
| 216 | +**Fecha de implementación:** 2025-10-25 |
| 217 | +**Autor:** Claude Code |
| 218 | +**Versión:** 1.0 |
0 commit comments