Diumenge, novembre 16, 2025
CLOUD & DEVOPS

Desplegar un lloc estàtic en S3 amb CloudFront i HTTPS

Publicar un lloc estàtic a AWS amb S3 i CloudFront sembla una tasca resolta des de fa anys. La documentació oficial el cobreix, hi ha tutorials arreu i la majoria d’administradors ho hem fet alguna vegada. El que no sol estar en aquestes guies són els detalls que acaben costant hores de feina: configuracions de permisos que no encaixen, comportaments poc intuïtius de CloudFront, certificats que caduquen en el pitjor moment, o decisions que es prenen ràpid i després s’arrosseguen durant anys.

1. El bucket S3 i les seves configuracions

Un bucket S3 per a un lloc estàtic sembla senzill: crear, activar “Static website hosting” i pujar els arxius. A la pràctica, gairebé sempre convé plantejar-se tres coses des de l’inici:

1.1 Nom del bucket

Si s’ha de fer servir el lloc en un domini propi (exemple.com), fer servir un bucket amb el mateix nom exacte que el domini principal estalvia passos posteriors. AWS té certes integracions que ho esperen. Si es fa servir un nom diferent, no és un error fatal, però obliga a treballar més amb redireccions o configuracions addicionals a CloudFront.

1.2 Bloqueig d’ accés públic

Des de fa uns anys, AWS bloqueja per defecte l’accés públic a buckets. Està bé, perquè la majoria de filtracions de dades en S3 es deuen a configuracions obertes. Per a un lloc públic, no s’ha de desactivar el bloqueig global. El recomanable és mantenir bloquejat l’accés públic i donar permisos únicament a través d’una Origin Access Control (OAC) de CloudFront. Això evita que algú se salti CloudFront i accedeixi directament al bucket, cosa que es tradueix en control centralitzat, mètriques coherents i seguretat.

1.3 Índex y document d’error

Els dos camps bàsics de “Static website hosting”: index.html i error.html. El detall que pot molestar: quan se serveix el lloc via CloudFront amb OAC, aquestes configuracions deixen de tenir efecte directe, perquè CloudFront no respecta la pàgina d’error definida en S3. Això significa que si volem un 404.html personalitzat, cal gestionar-lo des de les pàgines d’error de CloudFront. No convé oblidar-ho, perquè en un desplegament simple pot passar desapercebut fins que els usuaris ho reporten.

2. CloudFront com a interfície

CloudFront és potent però ple de configuracions que s’hereten de versions passades. El bàsic sembla clar: crear una distribució, apuntar al bucket i posar el certificat TLS. Però convé tenir en compte alguns detalls.

2.1 Triar l’ origen

La millor pràctica actual és fer servir un S3 bucket amb OAC en lloc de l’endpoint de “static website hosting”. L’endpoint clàssic (http://bucket.s3-website-region.amazonaws.com) és més simple per a proves, però obliga a deixar el bucket obert públicament. En producció, sempre OAC.

2.2 Polítiques de caché

Per defecte, CloudFront tendeix a cachejar de manera agressiva. Això pot semblar un avantatge, fins que es puja un index.html nou i els usuaris continuen veient la versió antiga durant hores. La pràctica habitual:

  • Definir una política de caché amb TTL baix (ex. 60 segons) per index.html.
  • Permetre TTL més llargs (1 dia o més) per a recursos estàtics versionats (CSS, JS, imatges amb hash en el nom). Això evita haver d’invalidar tota la distribució cada vegada que es desplega.

2.3 Compressió automàtica

Activar GZIP i Brotli des de CloudFront estalvia ample de banda i millora temps de càrrega. És un simple check, però moltes guies l’ometen perquè durant anys no existia. Avui, no hi ha raó per deixar-lo desactivat.

2.4 Logging

Configurar logs en S3 des del principi no és car i ajuda en auditories o diagnòstics de problemes. El recomanable és enviar-los a un bucket separat, amb un cicle de vida que els arxivi a Glacier als 30 dies. És un cost mínim i evita la situació típica d’hi va haver un pic estrany de tràfic la setmana passada i no tenim registres.

2.5 Comportament per defecte de rutes i redireccions

CloudFront no interpreta directoris com un servidor web clàssic (nginx, Apache). Si un usuari accedeix a /docs/ i no existeix un index.html en aquesta ruta exacta dins de S3, respondrà amb un error 403. Això trenca molts llocs migrats des de servidors tradicionals on /docs/ redirigia implícitament a /docs/index.html.La solució sol ser:

  • Estructurar el lloc amb rutes absolutes clares (/docs/index.html).
  • O bé afegir funcions Lambda@Edge o CloudFront Functions per interceptar peticions i normalitzar-les.

El segon enfocament complica el desplegament, però és l’única manera si es vol preservar URLs històriques.

2.6 CloudFront Functions vs Lambda@Edge

Molta gent fica funcions en Lambda@Edge només per fer redireccions bàsiques. És més car, més lent i més complex de gestionar (replicació a totes les regions). Per a transformacions simples d’ URL o capçaleres, CloudFront Functions és suficient. S’ha de reservar Lambda@Edge només per a casos on es necessiti lògica més pesada (per exemple, validació de cookies o renderitzat dinàmic parcial).

2.7 Polítiques d’ origen

Des que AWS va introduir les Origin Request Policies, convé ser explícit en quines capçaleres, cookies i paràmetres query s’envien a l’origen. Si es deixa el valor per defecte (“all”), CloudFront passa massa informació innecessària al bucket. Això afecta el rendiment i la caché: cada query diferent genera un objecte diferent a CloudFront encara que al bucket sigui sempre el mateix arxiu. L’habitual en un lloc estàtic és enviar res a l’origen, tret que hi hagi excepcions puntuals.

3. HTTPS i certificats

Aquí és on més temps sol perdre’s, sobretot quan es treballa amb diversos dominis i subdominis.

3.1 Certificats en AWS Certificate Manager (ACM)

El certificat ha d’ estar a la regió us-east-1 perquè CloudFront ho accepti. Aquest detall genera confusió: un demana un certificat a la regió de treball habitual (ex. eu-west-1) i després CloudFront no el mostra com a opció. No és un bug: CloudFront només consulta certificats de N. Virginia.

La solució és directa: tots els certificats de dominis públics que s’ hagin de fer servir a CloudFront s’ han de crear en us-east-1.

3.2 Validació DNS vs Email

La validació per correu continua existint, però és una mala idea en gairebé tots els casos: depèn de bústies que poden no estar actives o que canvien amb el temps. La validació per DNS (CNAME a Route 53 o un altre proveïdor) és la que evita problemes a futur. Es configura un cop i ACM renova el certificat de forma automàtica.

3.3 Renegociacions i expiració

Si s’oblida la validació DNS i s’opta per email, el certificat expira i el lloc queda inaccessible. En un entorn amb CloudFront, la caiguda no és immediata (sol trigar unes hores a propagar-se), cosa que confon al diagnosticar. Per evitar-ho, s’ha de fer servir sempre DNS i sempre automatitzat. Cap certificat d’un lloc públic hauria de dependre d’obrir un correu manualment cada any.

4. DNS i Route 53

Configurar el domini perquè apunti a CloudFront no és complicat, però hi ha un parell de trampes.

4.1 Àlies vs CNAME

Route 53 permet fer servir un Àlies cap a la distribució de CloudFront, en lloc d’un CNAME. Això és important perquè:

  1. Els Àlies són gratis (no cobren query addicional).
  2. Permeten fer servir el domini arrel (exemple.com) sense necessitat d’un redirect intermedi.

Si el domini està en un altre proveïdor, s’ha de fer servir un CNAME al nom de CloudFront, però es perd aquest avantatge.

4.2 TTL

Tot i que Route 53 permet TTL baixos, a CloudFront el temps de propagació de canvis continua sent diverses hores. No convé confiar-se: qualsevol migració de distribució o canvi de certificat s’ ha de planificar amb prou marge.

5. Gestió d’errors i pàgines personalitzades

Com es va comentar abans, S3 no controla els errors quan CloudFront està davant. Per a un lloc amb index.html tipus SPA, el típic és necessitar que totes les rutes no trobades rediriïn al index.html.

La configuració més estable a CloudFront és:

  • Error 403 i 404 → redirigir a /index.html amb codi 200.Això evita que una ruta com /app/dashboard mostri un 404 quan en realitat l’aplicació necessita que se serveixi el mateix HTML base.

6. Desplegament i invalidacions

Cada desplegament de lloc estàtic sovint implica pujar arxius nous i eliminar els antics.

6.1 Versionat d’ assets

Si els assets tenen noms versionats (exemple app.3f2a9c.css), CloudFront pot cachejar-los indefinidament. Això elimina gairebé per complet la necessitat d’invalidar. En canvi, si se serveixen com a style.css, caldrà invalidar després de cada desplegament, la qual cosa és lenta i amb cost addicional en distribucions amb tràfic alt.

6.2 Invalidacions selectives

Invalidar /* sembla l’opció més ràpida, però no escala. És preferible invalidar només index.html o els arxius que canvien sense hash en el nom. Això redueix la latència post-desplegament i el cost de les invalidacions (les primeres 1.000 són gratis, després es facturen).

6.3 Estratègia de desplegament gradual

Quan el lloc té tràfic elevat, convé evitar invalidacions grans perquè poden produir un “split brain”: part dels usuaris reben la versió nova, d’altres l’antiga, i el backend (si hi ha APIs) no sempre és compatible amb totes dues. Un patró més estable:

  • Pujar assets versionats (amb hash).
  • Pujar pàgines secundàries.
  • Pujar index.html en últim lloc.
  • Invalidar només index.html.

D’aquesta manera, quan el navegador recarregui l’HTML, tots els assets ja estan disponibles al bucket i a CloudFront.

6.4 Quan sí invalidar en bloc

En alguns casos no queda més remei que invalidar /*:

  • Si es canvia l’estructura de carpetes del lloc.
  • Si s’eliminen recursos antics que ja no han de ser accessibles.
  • Si es modifica un asset sense canviar-li el nom (la qual cosa s’hauria d’evitar sempre, però de vegades passa per limitacions de frameworks).

El consell pràctic: reservar /* com a últim recurs, i planificar-lo per endavant per a desplegaments de baix tràfic.

7. Seguretat i accessos directes

7.1 OAC vs OAI

AWS va introduir Origin Access Control (OAC) com a reemplaçament dels Origin Access Identity (OAI) antics. Tot i que tots dos continuen funcionant, convé migrar a OAC perquè:

  • Suporta firmes més modernes (SigV4).
  • Permet integrar capçaleres personalitzades.
  • És el camí de futur (OAI acabarà sent obsolet).

Molts tutorials antics continuen fent servir OAI, i és fàcil caure en aquesta trampa en copiar configuracions. El correcte avui és OAC.

7.2 Headers de seguretat

CloudFront no afegeix capçaleres de seguretat per defecte (Content-Security-Policy, Strict-Transport-Security, X-Frame-Options, etc.). En un entorn sense backend, això se sol resoldre amb un Lambda@Edge o CloudFront Function que afegeixi capçaleres. No és opcional: navegadors moderns penalitzen llocs sense HSTS o CSP. Un exemple mínim: afegir Strict-Transport-Security: max-age = 31536000; includeSubDomains; preload. Això força HTTPS en tot el domini i preveu downgrades.

7.3 Bloqueig d’ accés directe a S3

Encara que el bucket estigui privat, cal revisar que no existeixin endpoints públics antics. Molts equips creen un bucket, el deixen obert, configuren CloudFront, i després “tanquen” el bucket. Els enllaços públics poden quedar cachejats en cercadors o recursos compartits, la qual cosa provoca errors intermitents en usuaris que accedeixen directament al bucket. Convé bloquejar des de l’ inici i mai exposar la URL del bucket a usuaris finals.

8. Costos i sorpreses de facturació

8.1 Ample de banda sortint

El cost principal de CloudFront és l’ample de banda de sortida. Tot i que és més barat que servir directament des de S3, la factura creix ràpid si hi ha descàrregues grans (exemple: un lloc amb PDFs pesants). Convé activar mètriques detallades a CloudWatch i alarmes d’ ús d’ ample de banda. Esperar al final del mes és una mala idea: ja és tard.

8.2 Requests petits

CloudFront factura també per nombre de requests. Un lloc amb milers d’arxius estàtics petits (per exemple, imatges SVG individuals per a icones) pot generar més cost per requests que per ample de banda. Solució pràctica: consolidar recursos. Fer servir sprites, empaquetar JS/CSS, o almenys aplicar hashing i caching perquè el navegador els reutilitzi.

8.3 Invalidacions

Les primeres 1.000 invalidacions al mes són gratis, però un pipeline mal dissenyat pot gastar centenars per desplegament. En un entorn CI/CD amb diversos desplegaments al dia, es pot multiplicar per desenes de milers. En aquests casos és gairebé obligat aplicar versionat d’assets i limitar invalidacions a index.html.

9. Automatització del desplegament

9.1 aws s3 sync i les seves trampes

El comandament aws s3 sync sembla perfecte, però hi ha detalls:

  • Per defecte, puja arxius però no elimina els antics. Això deixa escombraries al bucket i pot generar conflictes (per exemple, un script.js antic que encara és accessible).
  • Per netejar, s’ha de fer servir –delete, però cal aplicar-ho amb cura perquè esborra sense preguntar.
  • També convé sempre especificar –exact-timestamps, altrament re-pujarà arxius sense canvis.

Un pipeline ben definit pot veure’s així:

AWS S3 Sync ./build s3://mi-bucket --exact-timestamps --delete

9.2 Hashing d’ arxius en pipelines

Molts frameworks (React, Vue, Angular, Next.js estàtic) generen assets amb hash automàticament. Però no tots els pipelines els aprofiten bé. És important pujar primer els arxius amb hash, després l’ index.html. Si es fa al revés, alguns usuaris poden rebre un index.html que apunta a un asset que encara no està disponible.

9.3 CI/CD i secrets

En entorns amb CI/CD (GitHub Actions, GitLab, Jenkins), l’habitual és injectar credencials AWS per pujar a S3. El correcte avui és fer servir rols amb OIDC i no secrets estàtics. Això elimina la necessitat de guardar AWS_ACCESS_KEY_ID i AWS_SECRET_ACCESS_KEY al pipeline. Menys exposició, menys rotació manual.

9.4 Invalidacions automatitzades

El pas final del pipeline sol ser:

AWS CloudFront create-invalidation --distribution-id DIST_ID --paths "/index.html"

Convé que estigui separat de la sincronització d’ arxius, per poder invalidar manualment sense redeploy si es detecta una fallada.

10. Diagnòstic de problemes comuns

10.1 AccessDenied des de CloudFront

Aquest error té diverses causes possibles, i la majoria d’administradors perden temps provant a l’atzar. Checklist típic:

  • El bucket està privat? Bé.
  • Hi ha OAC configurat?
  • La policy de bucket permet accés a l’OAC?
  • El nom de l’arxiu coincideix exactament, incloent majúscules/minúscules? S3 és case-sensitive.

Aquest últim punt és més freqüent del que sembla: pujar Index.html en lloc de index.html i trobar-se amb AccessDenied en intentar carregar l’arrel.

10.2 Canvis que no es veuen

Si el desplegament ha estat correcte però els usuaris no veuen els canvis:

  • Revisar política de caché a CloudFront.
  • Revisar capçaleres de caché que el navegador pugui estar respectant.
  • Revisar invalidacions. Un truc pràctic: afegir un querystring temporal (?v=123) a l’asset i veure si canvia. Si funciona, és un problema de caché, no de desplegament.

10.3 SSL/TLS handshake failure

Si uns usuaris reporten que no poden accedir per HTTPS però d’altres sí, revisar la compatibilitat de TLS. CloudFront permet configurar el mínim TLS (1.0, 1.1, 1.2). A la pràctica, deixar-lo a TLS 1.2 garanteix compatibilitat amb navegadors moderns i evita atacs obsolets. No convé baixar a 1.0 només perquè “hi ha un client amb IE8”. Si això passa, millor plantejar un endpoint alternatiu controlat.

10.4 Pàgines d’ error que no apareixen

Configurar a CloudFront que un 404 retorni /404.html i seguir veient l’error genèric és comú. Raons típiques:

  • L’arxiu /404.html no existeix o està en minúscules.
  • L’error original no és 404, sinó 403 (molt comú en buckets privats). La diferència entre 403 i 404 en S3 importa: si l’objecte no existeix, S3 en retorna 404; si l’objecte existeix però no és accessible, en retorna 403. En tots dos casos, cal mapejar-los a CloudFront.

10.5 Objectes orfes accessibles

Després de diversos desplegaments, poden quedar objectes antics accessibles en URLs que ja no es fan servir. Si contenen informació sensible (exemple: un .env pujat per error), CloudFront els servirà igual. Solució pràctica:

  • Tenir un procés de neteja automàtica amb aws s3 sync –delete.
  • Revisar periòdicament el bucket amb s3 ls o un inventari S3 habilitat.

10.6 Problemes de CORS

Tot i que un lloc estàtic sembla no necessitar CORS, apareixen quan se serveixen fonts (.woff2, .ttf) o s’integren APIs externes. Exemple clàssic: el navegador bloqueja una font perquè la capçalera Access-Control-Allow-Origin no hi és present. Hi ha dues formes de resoldre-ho:

  • Configurar regles CORS al bucket S3 (per permetre * o dominis concrets).
  • Afegir capçaleres des de CloudFront mitjançant una Response Headers Policy.

El segon enfocament és més recomanable perquè centralitza el control i evita tocar S3 cada vegada.

11. Exemples pràctics de configuracions

11.1 Policy de bucket mínima amb OAC

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

Amb això, el bucket només accepta requests signats per aquesta distribució concreta.

11.2 Política de caixet diferenciada

  • Per index.html:
    • TTL mínim: 0
    • TTL màxim: 60
    • Respectar capçaleres Cache-Control si existeixen
  • Per actius (*.js, *.css, *.png):
    • TTL mínim: 1h
    • TTL màxim: 30d
    • Omet cadenes de consulta

Això redueix invalidacions i accelera l’experiència d’usuari.

11.3 Capçaleres de seguretat a CloudFront

Capçaleres de resposta Política típica:

  • Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  • Content-Security-Policy: default-src ‘self’ (ajustar segons el lloc)
  • X-Content-Type-Options: nosniff
  • Referrer-Policy: same-origin

No tots els projectes les apliquen, però cada vegada més navegadors ho exigeixen.

12. Migració des de servidors tradicionals

Molts desplegaments de S3+CloudFront no són projectes nous, sinó migracions des de servidors Apache, nginx o fins i tot CDNs anteriors.

12.1 Problema d’URLs “amigables”

En un servidor clàssic, /blog/article podia resoldre’s amb una regla interna de reescriptura (RewriteRule) que servia /blog/article.html. A S3/CloudFront no existeix aquesta flexibilitat. Opcions:

  • Renomenar arxius a què coincideixin amb les rutes exactes.
  • Fer servir CloudFront Functions per afegir .html a les requests que no el tinguin.

El segon enfocament manté compatibilitat retroactiva, però afegeix complexitat.

12.2 Redireccions permanents

En migracions de dominis, és comú necessitar redirigir www.domini.com → domini.com. Es pot fer de dues formes:

  • Crear una distribució CloudFront específica per a www que redirigeixi al domini arrel.
  • Fer servir Route 53 i un bucket S3 “buit” configurat com a redirecció.

L’opció (1) manté tot a CloudFront i evita accessos directes a S3. La (2) és més barata, però menys consistent.

12.3 Logs i mètriques

Un servidor clàssic tenia access logs a /var/log/nginx/access.log. En migrar a CloudFront, es perden aquests logs si no s’habilita el logging explícit. La pràctica recomanable és activar Access Logs a CloudFront cap a un bucket dedicat, i activar CloudFront Real-Time Logs si es necessita depuració minut a minut. Són més cars, però estalvien hores d’incertesa en un incident.

13. Recomanacions de manteniment a llarg termini

13.1 Renovació de certificats

Tot i que ACM renova certificats automàticament, convé auditar cada 6 mesos que els registres DNS de validació segueixen presents. He vist casos en què algú “neteja” entrades antigues de Route 53 i trenca la cadena de renovació. Un simple script que verifiqui amb openssl s_client o aws acm describe-certificate és suficient.

13.2 Monitorització

  • CloudWatch Alarms en 5xxErrorRate de CloudFront.
  • Mètriques d’ ample de banda per evitar sorpreses de cost.
  • Health checks externs (Pingdom, UptimeRobot) per comprovar que HTTPS respon i el certificat és vàlid.

13.3 Documentació del procés

L’stack és senzill, però té particularitats (ex. certificats en us-east-1). Documentar aquests punts en el repo o en un runbook intern evita que algú repeteixi errors coneguts.

14. Conclusió

El desplegament de llocs estàtics en S3 + CloudFront + HTTPS sembla un tema resolt, però continua tenint matisos que afecten en producció. Els errors típics són sempre els mateixos: certificats en regió equivocada, caché que mai s’ invalida, buckets exposats per descuit, o DNS mal configurat. El que diferencia un desplegament problemàtic d’un estable és cobrir aquests detalls des del principi: OAC, polítiques de caché clares, assets versionats, logs actius i certificats validats per DNS.

Amb aquest enfocament, l’stack és molt estable i, tret de desplegaments regulars, a penes requereix manteniment operatiu.

Deixa un comentari

L'adreça electrònica no es publicarà. Els camps necessaris estan marcats amb *