Skip to content

Commit 5d6c58d

Browse files
authored
Merge pull request #47 from PythonMexico/staging
Staging
2 parents e46feaa + 27f5ab5 commit 5d6c58d

File tree

6 files changed

+396
-3
lines changed

6 files changed

+396
-3
lines changed

FIX_404_ERRORS.md

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
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

docs/URL_FIX_DOCUMENTATION.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Fix para URLs sin extensión .html en producción
2+
3+
## Problema
4+
Los enlaces funcionaban correctamente en local pero no en producción. Por ejemplo:
5+
-`https://pythoncdmx.org/meetups/` no funcionaba
6+
-`https://pythoncdmx.org/meetups/index.html` sí funcionaba
7+
8+
## Causa
9+
El problema se debía a que CloudFront no estaba configurado para manejar URLs sin extensión `.html`. Cuando MkDocs genera el sitio con `use_directory_urls: true`, crea URLs como `/meetups/` que apuntan a `/meetups/index.html`, pero CloudFront no sabía cómo resolver estas URLs.
10+
11+
## Solución Implementada
12+
13+
### 1. Configuración en mkdocs.yml
14+
```yaml
15+
# URL configuration
16+
use_directory_urls: true
17+
```
18+
19+
### 2. CloudFront Function
20+
Se agregó una función CloudFront que maneja automáticamente las URLs sin extensión:
21+
22+
```javascript
23+
function handler(event) {
24+
var request = event.request;
25+
var uri = request.uri;
26+
27+
// If the URI ends with a slash, append index.html
28+
if (uri.endsWith('/')) {
29+
request.uri = uri + 'index.html';
30+
}
31+
// If the URI doesn't have an extension, append /index.html
32+
else if (!uri.includes('.') && !uri.endsWith('/')) {
33+
request.uri = uri + '/index.html';
34+
}
35+
36+
return request;
37+
}
38+
```
39+
40+
### 3. Asociación con Cache Behaviors
41+
La función se asoció con todos los cache behaviors de CloudFront para asegurar consistencia.
42+
43+
## Archivos Modificados
44+
45+
1. **mkdocs.yml**: Agregada configuración `use_directory_urls: true`
46+
2. **terraform/cloudfront.tf**:
47+
- Agregada CloudFront Function `url_rewrite`
48+
- Asociada la función con todos los cache behaviors
49+
3. **terraform/cloudfront-staging.tf**:
50+
- Aplicada la misma CloudFront Function a staging
51+
- Asociada la función con todos los cache behaviors de staging
52+
53+
## Despliegue
54+
55+
Para aplicar estos cambios:
56+
57+
1. **Aplicar cambios de Terraform**:
58+
```bash
59+
cd terraform
60+
terraform plan
61+
terraform apply
62+
```
63+
64+
2. **Desplegar el sitio**:
65+
```bash
66+
# Los cambios se aplicarán automáticamente en el próximo deploy
67+
git push origin main
68+
```
69+
70+
## Verificación
71+
72+
Después del despliegue, verificar que funcionen:
73+
74+
### Producción
75+
-`https://pythoncdmx.org/meetups/`
76+
-`https://pythoncdmx.org/meetups/index.html`
77+
-`https://pythoncdmx.org/about/`
78+
-`https://pythoncdmx.org/about/index.html`
79+
80+
### Staging
81+
-`https://staging.pythoncdmx.org/meetups/`
82+
-`https://staging.pythoncdmx.org/meetups/index.html`
83+
-`https://staging.pythoncdmx.org/about/`
84+
-`https://staging.pythoncdmx.org/about/index.html`
85+
86+
## Notas Técnicas
87+
88+
- La CloudFront Function se ejecuta en el edge, por lo que tiene latencia mínima
89+
- La función solo modifica la URI si es necesario, sin afectar assets estáticos
90+
- Los cache behaviors mantienen sus configuraciones originales de TTL
91+
- La solución es compatible con el comportamiento existente de MkDocs
92+
93+
## Referencias
94+
95+
- [MkDocs use_directory_urls documentation](https://www.mkdocs.org/user-guide/configuration/#use_directory_urls)
96+
- [CloudFront Functions documentation](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html)

mkdocs.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ repo_url: https://github.com/PythonMexico/pythonCDMX/
1111
# Copyright
1212
copyright: Copyright &copy; 2025 Python CDMX
1313

14+
# URL configuration
15+
use_directory_urls: true
16+
1417
# Theme configuration
1518
theme:
1619
name: material

terraform/cloudfront-staging.tf

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ resource "aws_cloudfront_origin_access_control" "website_staging" {
77
signing_protocol = "sigv4"
88
}
99

10+
# CloudFront Function for staging (reuse the same function as production)
11+
# Note: CloudFront Functions are global, so we can reference the same function
12+
1013
# CloudFront distribution for staging
1114
resource "aws_cloudfront_distribution" "website_staging" {
1215
enabled = true
@@ -42,6 +45,12 @@ resource "aws_cloudfront_distribution" "website_staging" {
4245
default_ttl = 300 # 5 minutes - shorter cache for staging
4346
max_ttl = 3600 # 1 hour - shorter cache for staging
4447
compress = true
48+
49+
# Associate CloudFront Function for URL rewriting (same as production)
50+
function_association {
51+
event_type = "viewer-request"
52+
function_arn = aws_cloudfront_function.url_rewrite.arn
53+
}
4554
}
4655

4756
# Cache behavior for static assets (CSS) - shorter cache for staging
@@ -63,6 +72,12 @@ resource "aws_cloudfront_distribution" "website_staging" {
6372
default_ttl = 1800 # 30 minutes
6473
max_ttl = 7200 # 2 hours
6574
compress = true
75+
76+
# Associate CloudFront Function for URL rewriting
77+
function_association {
78+
event_type = "viewer-request"
79+
function_arn = aws_cloudfront_function.url_rewrite.arn
80+
}
6681
}
6782

6883
# Cache behavior for static assets (JS) - shorter cache for staging
@@ -84,6 +99,12 @@ resource "aws_cloudfront_distribution" "website_staging" {
8499
default_ttl = 1800 # 30 minutes
85100
max_ttl = 7200 # 2 hours
86101
compress = true
102+
103+
# Associate CloudFront Function for URL rewriting
104+
function_association {
105+
event_type = "viewer-request"
106+
function_arn = aws_cloudfront_function.url_rewrite.arn
107+
}
87108
}
88109

89110
# Cache behavior for images - shorter cache for staging
@@ -105,6 +126,12 @@ resource "aws_cloudfront_distribution" "website_staging" {
105126
default_ttl = 3600 # 1 hour
106127
max_ttl = 86400 # 24 hours
107128
compress = true
129+
130+
# Associate CloudFront Function for URL rewriting
131+
function_association {
132+
event_type = "viewer-request"
133+
function_arn = aws_cloudfront_function.url_rewrite.arn
134+
}
108135
}
109136

110137
# Custom error responses for SPA-like behavior

0 commit comments

Comments
 (0)