· nervico-team · arquitectura-cloud  · 11 min read

CDN y CloudFront: configuración óptima para rendimiento web

Guía práctica de Amazon CloudFront: configuración paso a paso, optimización de caché, compresión, seguridad con WAF y costes reales para mejorar el rendimiento web.

Guía práctica de Amazon CloudFront: configuración paso a paso, optimización de caché, compresión, seguridad con WAF y costes reales para mejorar el rendimiento web.

Un usuario en Madrid que accede a un servidor en Virginia experimenta una latencia base de 120-150ms. Solo por la distancia física. Añade el tiempo de procesamiento del servidor, las consultas a base de datos y la transferencia de assets (imágenes, CSS, JavaScript), y la primera carga de la página puede tardar 3-5 segundos.

CloudFront reduce esa latencia a 10-30ms sirviendo el contenido desde el edge location más cercano al usuario. AWS opera más de 600 puntos de presencia en 100 ciudades de 50 países. La diferencia es perceptible: una página que carga en 1 segundo en lugar de 3 no solo mejora la experiencia de usuario, sino que mejora directamente las métricas de conversión. Google ha documentado que cada 100ms adicionales de latencia reducen las ventas en un 1%.

Este artículo explica cómo configurar CloudFront de forma óptima, con los parámetros que realmente importan, los errores de configuración que anulan los beneficios del CDN, y los costes reales que puedes esperar.

Cómo funciona CloudFront

El flujo de una petición

Cuando un usuario solicita un recurso a través de CloudFront, el flujo es:

  1. Resolución DNS: El navegador resuelve el dominio. Route 53 (o el DNS configurado) responde con la IP del edge location más cercano al usuario, basándose en latencia o geolocalización.
  2. Edge location: La petición llega al punto de presencia más cercano. CloudFront busca el recurso en su caché local.
  3. Cache hit: Si el recurso está en caché y no ha expirado, CloudFront lo devuelve directamente. Latencia mínima.
  4. Cache miss: Si el recurso no está en caché, CloudFront lo solicita al Regional Edge Cache (caché intermedia). Si tampoco está ahí, lo solicita al origen (S3, ALB, API Gateway, servidor custom).
  5. Respuesta: El recurso se devuelve al usuario y se almacena en caché para peticiones futuras.
Usuario (Madrid) → Edge Location (Madrid) → Regional Cache (Frankfurt)
                                                    ↓ (cache miss)
                                              Origin (S3 / ALB / API)

Orígenes soportados

CloudFront puede servir contenido desde múltiples tipos de origen:

  • Amazon S3: La opción más común para assets estáticos (HTML, CSS, JS, imágenes). Integración nativa con Origin Access Control (OAC).
  • Application Load Balancer (ALB): Para contenido dinámico servido por aplicaciones en ECS, EKS o EC2.
  • API Gateway: Para APIs REST o HTTP.
  • Lambda@Edge / CloudFront Functions: Para lógica de procesamiento en el edge (redirecciones, autenticación, personalización).
  • Origen custom: Cualquier servidor HTTP/HTTPS accesible por internet.

Configuración paso a paso

Distribución para sitio estático en S3

El caso más común: un sitio web estático (React, Next.js, Astro, Vue) desplegado en S3 con CloudFront como CDN.

# Terraform: Distribución CloudFront con S3
resource "aws_cloudfront_distribution" "website" {
  enabled             = true
  is_ipv6_enabled     = true
  default_root_object = "index.html"
  aliases             = ["www.tudominio.com"]
  price_class         = "PriceClass_100"
  http_version        = "http2and3"

  origin {
    domain_name              = aws_s3_bucket.website.bucket_regional_domain_name
    origin_id                = "S3Origin"
    origin_access_control_id = aws_cloudfront_origin_access_control.s3.id
  }

  default_cache_behavior {
    allowed_methods        = ["GET", "HEAD", "OPTIONS"]
    cached_methods         = ["GET", "HEAD"]
    target_origin_id       = "S3Origin"
    viewer_protocol_policy = "redirect-to-https"
    compress               = true

    cache_policy_id            = aws_cloudfront_cache_policy.optimized.id
    origin_request_policy_id   = data.aws_cloudfront_origin_request_policy.cors_s3.id
    response_headers_policy_id = aws_cloudfront_response_headers_policy.security.id
  }

  # SPA: redirigir 404 a index.html
  custom_error_response {
    error_code         = 403
    response_code      = 200
    response_page_path = "/index.html"
  }

  custom_error_response {
    error_code         = 404
    response_code      = 200
    response_page_path = "/index.html"
  }

  viewer_certificate {
    acm_certificate_arn      = aws_acm_certificate.cert.arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
}

# Origin Access Control (reemplaza a OAI)
resource "aws_cloudfront_origin_access_control" "s3" {
  name                              = "s3-oac"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

Notas sobre la configuración:

  • price_class = "PriceClass_100": Usa solo edge locations en Norteamérica y Europa. Reduce costes un 20-30% frente a PriceClass_All. Si tus usuarios están solo en Europa y América, no necesitas edge locations en Asia u Oceanía.
  • http_version = "http2and3": Habilita HTTP/2 y HTTP/3 (QUIC). HTTP/3 reduce la latencia de conexión inicial en un 30-40% porque usa UDP en lugar de TCP.
  • compress = true: Comprime automáticamente con Brotli o gzip los formatos text/html, application/javascript, text/css, etc. Reduce el tamaño de transferencia un 60-80%.

Políticas de caché

CloudFront usa Cache Policies para determinar qué parámetros de la petición afectan a la clave de caché. Una política bien configurada maximiza el cache hit ratio.

resource "aws_cloudfront_cache_policy" "optimized" {
  name        = "optimized-cache-policy"
  min_ttl     = 0
  default_ttl = 86400    # 24 horas
  max_ttl     = 31536000 # 1 año

  parameters_in_cache_key_and_forwarded_to_origin {
    cookies_config {
      cookie_behavior = "none"  # No incluir cookies en la cache key
    }
    headers_config {
      header_behavior = "none"  # No incluir headers en la cache key
    }
    query_strings_config {
      query_string_behavior = "none"  # No incluir query strings
    }

    enable_accept_encoding_brotli = true
    enable_accept_encoding_gzip   = true
  }
}

La regla de oro: Cuantos menos parámetros incluyas en la cache key, mayor será el cache hit ratio. Cada parámetro adicional (cookie, header, query string) crea una variante diferente en caché.

Para assets estáticos con fingerprinting (ej: app.a1b2c3.js), el query string no importa porque el hash está en el nombre del archivo. Para APIs, necesitas incluir query strings relevantes.

Distribución para APIs dinámicas

Para contenido dinámico (APIs, SSR), la configuración cambia:

# Cache behavior para API
ordered_cache_behavior {
  path_pattern           = "/api/*"
  allowed_methods        = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
  cached_methods         = ["GET", "HEAD"]
  target_origin_id       = "ALBOrigin"
  viewer_protocol_policy = "https-only"
  compress               = true

  # No cachear por defecto; respetar Cache-Control del origen
  cache_policy_id          = data.aws_cloudfront_cache_policy.caching_disabled.id
  origin_request_policy_id = data.aws_cloudfront_origin_request_policy.all_viewer.id
}

Principio: Para APIs, desactiva el caché de CloudFront por defecto y deja que el origen controle el comportamiento con headers Cache-Control. CloudFront respeta Cache-Control: max-age=0, no-cache, no-store.

Para endpoints que devuelven datos que cambian con poca frecuencia (catálogo de productos, configuración), puedes activar caché con un TTL corto:

Cache-Control: public, max-age=300, s-maxage=600

Esto indica: el navegador cachea 5 minutos, CloudFront cachea 10 minutos.

Optimización avanzada

Cache invalidation vs versioning

Cuando actualizas contenido, tienes dos opciones para que los usuarios vean la versión nueva:

Invalidation: Solicitas a CloudFront que elimine objetos específicos de todas las edge locations.

aws cloudfront create-invalidation \
  --distribution-id E1234567890 \
  --paths "/index.html" "/css/*" "/js/*"

Problemas: Las invalidaciones tardan 5-15 minutos en propagarse globalmente. Las primeras 1.000 rutas/mes son gratuitas; después, 0,005 dolares por ruta.

Versioning (recomendado): Incluye un hash o versión en el nombre del archivo. Los frameworks modernos hacen esto automáticamente.

app.a1b2c3d4.js  (versión actual)
app.e5f6g7h8.js  (versión nueva)

Con versioning, el nuevo archivo tiene un nombre diferente. CloudFront lo solicita al origen como un recurso nuevo. No necesitas invalidar nada. El archivo antiguo se sirve desde caché hasta que expira, pero nadie lo solicita porque el HTML referencia la versión nueva.

Excepción: index.html no puede tener versionado en el nombre porque es el punto de entrada. Para este archivo, configura un TTL corto (60-300 segundos) o usa invalidación después de cada despliegue.

CloudFront Functions vs Lambda@Edge

CloudFront ofrece dos formas de ejecutar código en el edge:

CaracterísticaCloudFront FunctionsLambda@Edge
RuntimeJavaScript (limitado)Node.js, Python
Memoria2 MB128 MB - 10 GB
Duración máxima1 ms5s (viewer) / 30s (origin)
EventosViewer request/responseViewer + origin request/response
Coste0,10 $ por millón0,60 $ por millón + duración
Acceso a redNoSi

CloudFront Functions para:

  • Redirecciones URL (www a non-www, HTTP a HTTPS)
  • Manipulación de headers (añadir security headers)
  • Reescritura de URL (clean URLs, trailing slashes)
  • A/B testing basado en cookies
// CloudFront Function: añadir security headers
function handler(event) {
  var response = event.response;
  var headers = response.headers;

  headers['strict-transport-security'] = {
    value: 'max-age=63072000; includeSubdomains; preload',
  };
  headers['x-content-type-options'] = { value: 'nosniff' };
  headers['x-frame-options'] = { value: 'DENY' };
  headers['x-xss-protection'] = { value: '1; mode=block' };
  headers['referrer-policy'] = { value: 'strict-origin-when-cross-origin' };

  return response;
}

Lambda@Edge para:

  • Autenticación y autorización
  • Generación de contenido dinámico en el edge
  • A/B testing con lógica compleja
  • Personalización por geolocalización

Compresión: Brotli vs gzip

CloudFront soporta compresión automática con Brotli y gzip. Brotli ofrece un 15-25% mejor ratio de compresión que gzip para contenido web, pero solo funciona sobre HTTPS (que debería ser el 100% de tu tráfico).

Tipo de archivoSin comprimirgzipBrotliAhorro Brotli vs gzip
HTML (100 KB)100 KB25 KB20 KB20% menor
JavaScript (500 KB)500 KB120 KB95 KB21% menor
CSS (80 KB)80 KB18 KB14 KB22% menor
JSON (200 KB)200 KB45 KB36 KB20% menor

CloudFront selecciona automáticamente el mejor algoritmo basándose en el header Accept-Encoding del navegador. No necesitas configurar nada más allá de compress = true.

Seguridad con CloudFront

AWS WAF

CloudFront se integra nativamente con AWS WAF (Web Application Firewall) para proteger contra ataques comunes:

resource "aws_wafv2_web_acl" "cloudfront" {
  name  = "cloudfront-waf"
  scope = "CLOUDFRONT"

  default_action {
    allow {}
  }

  # Protección contra SQL injection
  rule {
    name     = "aws-sql-injection"
    priority = 1

    override_action { none {} }

    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesSQLiRuleSet"
        vendor_name = "AWS"
      }
    }

    visibility_config {
      sampled_requests_enabled   = true
      cloudwatch_metrics_enabled = true
      metric_name                = "sql-injection"
    }
  }

  # Rate limiting: 2000 peticiones por IP en 5 minutos
  rule {
    name     = "rate-limit"
    priority = 2

    action { block {} }

    statement {
      rate_based_statement {
        limit              = 2000
        aggregate_key_type = "IP"
      }
    }

    visibility_config {
      sampled_requests_enabled   = true
      cloudwatch_metrics_enabled = true
      metric_name                = "rate-limit"
    }
  }

  visibility_config {
    sampled_requests_enabled   = true
    cloudwatch_metrics_enabled = true
    metric_name                = "cloudfront-waf"
  }
}

Coste de WAF: 5 dolares/mes por Web ACL + 1 dolar/mes por regla + 0,60 dolares por millón de peticiones evaluadas. Para un sitio con 10 millones de peticiones al mes y 5 reglas, el coste es aproximadamente 16 dolares/mes.

Origin Access Control (OAC)

Si tu origen es S3, OAC garantiza que los usuarios solo pueden acceder al contenido a través de CloudFront. El bucket S3 no es accesible directamente por URL.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontServicePrincipal",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::mi-bucket-web/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::123456789:distribution/E1234567890"
        }
      }
    }
  ]
}

HTTPS y certificados

CloudFront requiere certificados SSL/TLS de AWS Certificate Manager (ACM). Los certificados de ACM son gratuitos y se renuevan automáticamente. El único requisito es que el certificado debe estar en la región us-east-1 (requisito global de CloudFront).

Costes reales de CloudFront

Modelo de facturación

CloudFront cobra por tres conceptos:

  1. Transferencia de datos al usuario: Desde 0,085 $/GB (primeros 10 TB/mes) hasta 0,020 $/GB (más de 5 PB/mes).
  2. Peticiones HTTP/HTTPS: 0,0075 $ por 10.000 peticiones HTTPS (Norteamérica/Europa).
  3. Invalidaciones: Primeras 1.000 rutas/mes gratis; después, 0,005 $ por ruta.

Escenarios de coste

EscenarioTráficoPeticionesCoste mensual
Blog/landing page50 GB2M5-8 $
Aplicación SaaS media500 GB20M50-70 $
E-commerce con assets pesados5 TB100M450-550 $
Plataforma de streaming50 TB500M3.500-4.500 $

Ahorro vs servir desde el origen

CloudFront no solo mejora la latencia; también puede reducir costes. La transferencia de datos desde S3 directamente cuesta 0,09 $/GB. Desde CloudFront, los primeros 10 TB cuestan 0,085 $/GB, pero 1 TB gratuito al mes está incluido en el Free Tier. Además, las peticiones de CloudFront al origen (S3) son gratuitas.

Para sitios con tráfico mayoritariamente cacheable, CloudFront puede ser más barato que servir directamente desde S3 o ALB.

Monitorización y métricas

Métricas clave en CloudWatch

  • Cache hit ratio: El porcentaje de peticiones servidas desde caché. Objetivo: más del 90% para assets estáticos.
  • Error rate: Porcentaje de respuestas 4xx y 5xx. Objetivo: menos del 1%.
  • Latencia: Tiempo hasta el primer byte (TTFB). Objetivo: menos de 50ms para assets cacheados.
  • Bytes transferred: Volumen total de datos servidos. Para estimación de costes.
# Consultar cache hit ratio de las últimas 24 horas
aws cloudwatch get-metric-statistics \
  --namespace AWS/CloudFront \
  --metric-name CacheHitRate \
  --dimensions Name=DistributionId,Value=E1234567890 \
  --start-time $(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 3600 \
  --statistics Average

CloudFront Access Logs

Habilita los access logs para analizar patrones de tráfico, identificar contenido no cacheado y detectar abusos.

Los logs se almacenan en S3 y pueden analizarse con Athena:

SELECT
  date,
  time,
  sc_status,
  cs_uri_stem,
  x_edge_result_type,
  time_taken
FROM cloudfront_logs
WHERE x_edge_result_type = 'Miss'
  AND date = '2025-09-24'
ORDER BY time_taken DESC
LIMIT 20;

Errores de configuración frecuentes

Error 1: cachear contenido personalizado

Si tu página incluye el nombre del usuario en el HTML y la cacheas sin incluir la cookie de sesión en la cache key, un usuario verá el nombre de otro. Para contenido personalizado, usa Cache-Control: private, no-store o incluye un identificador en la cache key.

Error 2: TTL demasiado largo en index.html

Si configuras un TTL de 24 horas para index.html y despliegas una actualización, los usuarios verán la versión antigua durante hasta 24 horas. Configura un TTL de 60-300 segundos para archivos de entrada y usa invalidación post-despliegue.

Error 3: no habilitar compresión

CloudFront puede comprimir automáticamente el contenido, pero necesitas activar compress = true. Sin compresión, estás transfiriendo 3-5 veces más datos de los necesarios, pagando más en transferencia y ofreciendo peor experiencia.

Error 4: ignorar HTTP/3

HTTP/3 usa QUIC sobre UDP, lo que elimina el head-of-line blocking de HTTP/2 y reduce el tiempo de establecimiento de conexión. CloudFront soporta HTTP/3 sin coste adicional. Habilitarlo es una mejora gratuita de rendimiento.

Conclusión

CloudFront no es un complemento opcional. Para cualquier aplicación web que sirva a usuarios en múltiples ubicaciones geográficas, un CDN correctamente configurado es la mejora de rendimiento más impactante y más barata que puedes implementar.

La configuración óptima no es complicada, pero requiere atención a los detalles: políticas de caché restrictivas para maximizar el hit ratio, compresión habilitada, HTTP/3 activo, security headers en el edge y TTLs apropiados para cada tipo de contenido.

Si necesitas ayuda para optimizar la distribución de contenido de tu aplicación o configurar CloudFront para un caso de uso complejo, nuestro equipo de consultoría AWS tiene experiencia directa con estas configuraciones. Solicita una auditoría gratuita para evaluar el rendimiento actual de tu infraestructura web.

Back to Blog

Related Posts

View All Posts »