Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 218 additions & 0 deletions FIX_404_ERRORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
# Solución a Errores 404 en pythoncdmx.org

## 📋 Resumen del Problema

**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 (sin `index.html`)
- Como ese objeto no existe, S3 devuelve 403, que CloudFront convierte en 404

## ✅ Solución Implementada

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.

### Archivos Modificados/Creados

#### 1. **Nuevo:** `terraform/cloudfront-function.tf`
- Crea dos CloudFront Functions (producción y staging)
- Función JavaScript que intercepta requests y añade `index.html` automáticamente
- Maneja dos casos:
- URLs que terminan en `/` → Añade `index.html`
- URLs sin extensión de archivo → Añade `/index.html`

#### 2. **Modificado:** `terraform/cloudfront.tf`
- 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)

#### 3. **Modificado:** `terraform/cloudfront-staging.tf`
- Líneas 46-50: Asocia la función CloudFront de staging al `default_cache_behavior`
- Misma lógica aplicada al ambiente de staging

## 🚀 Pasos para Desplegar

### Prerequisitos
- Acceso a AWS con credenciales configuradas
- Terraform instalado
- Variables de Terraform configuradas (archivo `terraform.tfvars`)

### Despliegue

1. **Navega al directorio de Terraform:**
```bash
cd terraform
```

2. **Revisa el plan de Terraform:**
```bash
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)

3. **Aplica los cambios:**
```bash
terraform apply
```

4. **Confirma los cambios:** Escribe `yes` cuando se te solicite

### Tiempo de Propagación

- **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`)

### Invalidación de Caché (Opcional pero Recomendado)

Para aplicar los cambios inmediatamente sin esperar la expiración del caché:

```bash
# 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:
```bash
terraform output cloudfront_distribution_id
terraform output cloudfront_staging_distribution_id
```

## 🧪 Verificación

Una vez desplegado, verifica que las siguientes URLs funcionan:

### Producción (pythoncdmx.org)
- ✅ `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/`

### Staging (si aplica)
- ✅ Todas las rutas equivalentes en el dominio de staging

## 📊 Impacto y Beneficios

### Ventajas de la Solución
- ✅ **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

### Costo Estimado
- **CloudFront Functions:** ~$0.10 por millón de requests
- Para un sitio con 100,000 visitas/mes: **~$0.01/mes**

## 🔍 Debugging

Si después del despliegue aún hay errores 404:

1. **Verifica que la función esté asociada:**
```bash
aws cloudfront get-distribution --id <DISTRIBUTION_ID> \
| jq '.Distribution.DistributionConfig.DefaultCacheBehavior.FunctionAssociations'
```

2. **Verifica que la función esté publicada:**
```bash
aws cloudfront list-functions
```

3. **Revisa CloudWatch Logs (si está habilitado):**
```bash
aws logs tail /aws/cloudfront/function/pythoncdmx-directory-index --follow
```

4. **Invalida el caché de CloudFront** (ver comando arriba)

5. **Prueba con curl para ver headers:**
```bash
curl -I https://pythoncdmx.org/meetups/
```

## 📝 Notas Técnicas

### Cómo Funciona la CloudFront Function

```javascript
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:**
1. Usuario solicita `https://pythoncdmx.org/meetups/`
2. CloudFront recibe el request en la edge location
3. **CloudFront Function** intercepta y reescribe: `/meetups/` → `/meetups/index.html`
4. CloudFront solicita a S3: `s3://bucket/meetups/index.html`
5. S3 devuelve el archivo (existe en S3 gracias a MkDocs)
6. CloudFront devuelve la respuesta al usuario

### Alternativas Consideradas (No Implementadas)

1. **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

2. **Cambiar a `--no-directory-urls`:**
- ❌ URLs menos amigables (`/meetups.html`)
- ❌ Rompe links existentes
- ❌ Peor SEO

3. **S3 Redirects:**
- ❌ No funciona con CloudFront OAC
- ❌ Requiere S3 public (inseguro)

## 🎯 Próximos Pasos

1. **Desplegar los cambios** siguiendo la sección "Pasos para Desplegar"
2. **Verificar** que todas las URLs funcionan correctamente
3. **Monitorear** CloudFront metrics durante las primeras 24 horas
4. **Documentar** en el README del proyecto que se usa CloudFront Functions

## 🆘 Soporte

Si encuentras problemas durante el despliegue:

1. Revisa el output de `terraform plan` y `terraform apply`
2. Verifica los logs de CloudWatch (si están habilitados)
3. Consulta la documentación de AWS:
- [CloudFront Functions](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html)
- [CloudFront Distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html)

---

**Fecha de implementación:** 2025-10-25
**Autor:** Claude Code
**Versión:** 1.0
96 changes: 96 additions & 0 deletions docs/URL_FIX_DOCUMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Fix para URLs sin extensión .html en producción

## Problema
Los enlaces funcionaban correctamente en local pero no en producción. Por ejemplo:
- ❌ `https://pythoncdmx.org/meetups/` no funcionaba
- ✅ `https://pythoncdmx.org/meetups/index.html` sí funcionaba

## Causa
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.

## Solución Implementada

### 1. Configuración en mkdocs.yml
```yaml
# URL configuration
use_directory_urls: true
```

### 2. CloudFront Function
Se agregó una función CloudFront que maneja automáticamente las URLs sin extensión:

```javascript
function handler(event) {
var request = event.request;
var uri = request.uri;

// If the URI ends with a slash, append index.html
if (uri.endsWith('/')) {
request.uri = uri + 'index.html';
}
// If the URI doesn't have an extension, append /index.html
else if (!uri.includes('.') && !uri.endsWith('/')) {
request.uri = uri + '/index.html';
}

return request;
}
```

### 3. Asociación con Cache Behaviors
La función se asoció con todos los cache behaviors de CloudFront para asegurar consistencia.

## Archivos Modificados

1. **mkdocs.yml**: Agregada configuración `use_directory_urls: true`
2. **terraform/cloudfront.tf**:
- Agregada CloudFront Function `url_rewrite`
- Asociada la función con todos los cache behaviors
3. **terraform/cloudfront-staging.tf**:
- Aplicada la misma CloudFront Function a staging
- Asociada la función con todos los cache behaviors de staging

## Despliegue

Para aplicar estos cambios:

1. **Aplicar cambios de Terraform**:
```bash
cd terraform
terraform plan
terraform apply
```

2. **Desplegar el sitio**:
```bash
# Los cambios se aplicarán automáticamente en el próximo deploy
git push origin main
```

## Verificación

Después del despliegue, verificar que funcionen:

### Producción
- ✅ `https://pythoncdmx.org/meetups/`
- ✅ `https://pythoncdmx.org/meetups/index.html`
- ✅ `https://pythoncdmx.org/about/`
- ✅ `https://pythoncdmx.org/about/index.html`

### Staging
- ✅ `https://staging.pythoncdmx.org/meetups/`
- ✅ `https://staging.pythoncdmx.org/meetups/index.html`
- ✅ `https://staging.pythoncdmx.org/about/`
- ✅ `https://staging.pythoncdmx.org/about/index.html`

## Notas Técnicas

- La CloudFront Function se ejecuta en el edge, por lo que tiene latencia mínima
- La función solo modifica la URI si es necesario, sin afectar assets estáticos
- Los cache behaviors mantienen sus configuraciones originales de TTL
- La solución es compatible con el comportamiento existente de MkDocs

## Referencias

- [MkDocs use_directory_urls documentation](https://www.mkdocs.org/user-guide/configuration/#use_directory_urls)
- [CloudFront Functions documentation](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html)
3 changes: 3 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ repo_url: https://github.com/PythonMexico/pythonCDMX/
# Copyright
copyright: Copyright &copy; 2025 Python CDMX

# URL configuration
use_directory_urls: true

# Theme configuration
theme:
name: material
Expand Down
27 changes: 27 additions & 0 deletions terraform/cloudfront-staging.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ resource "aws_cloudfront_origin_access_control" "website_staging" {
signing_protocol = "sigv4"
}

# CloudFront Function for staging (reuse the same function as production)
# Note: CloudFront Functions are global, so we can reference the same function

# CloudFront distribution for staging
resource "aws_cloudfront_distribution" "website_staging" {
enabled = true
Expand Down Expand Up @@ -42,6 +45,12 @@ resource "aws_cloudfront_distribution" "website_staging" {
default_ttl = 300 # 5 minutes - shorter cache for staging
max_ttl = 3600 # 1 hour - shorter cache for staging
compress = true

# Associate CloudFront Function for URL rewriting (same as production)
function_association {
event_type = "viewer-request"
function_arn = aws_cloudfront_function.url_rewrite.arn
}
}

# Cache behavior for static assets (CSS) - shorter cache for staging
Expand All @@ -63,6 +72,12 @@ resource "aws_cloudfront_distribution" "website_staging" {
default_ttl = 1800 # 30 minutes
max_ttl = 7200 # 2 hours
compress = true

# Associate CloudFront Function for URL rewriting
function_association {
event_type = "viewer-request"
function_arn = aws_cloudfront_function.url_rewrite.arn
}
}

# Cache behavior for static assets (JS) - shorter cache for staging
Expand All @@ -84,6 +99,12 @@ resource "aws_cloudfront_distribution" "website_staging" {
default_ttl = 1800 # 30 minutes
max_ttl = 7200 # 2 hours
compress = true

# Associate CloudFront Function for URL rewriting
function_association {
event_type = "viewer-request"
function_arn = aws_cloudfront_function.url_rewrite.arn
}
}

# Cache behavior for images - shorter cache for staging
Expand All @@ -105,6 +126,12 @@ resource "aws_cloudfront_distribution" "website_staging" {
default_ttl = 3600 # 1 hour
max_ttl = 86400 # 24 hours
compress = true

# Associate CloudFront Function for URL rewriting
function_association {
event_type = "viewer-request"
function_arn = aws_cloudfront_function.url_rewrite.arn
}
}

# Custom error responses for SPA-like behavior
Expand Down
Loading