CloudFront devant S3 (HTTPS et cache)
À la fin de ce tutoriel, vous servirez un site/objet S3 privé via CloudFront avec Origin Access Control (OAC), HTTPS sur le domaine
*.cloudfront.net, et une invalidation de cache — pattern production 2026 (plus de bucket public website).Niveau : Intermédiaire · Temps estimé : 60–75 min · Versions testées : CloudFront + S3 OAC 2026, AWS CLI v2 · Dernière vérification : 2026-09-11 · Bucket Region :
ca-central-1(CloudFront est global)Slug :
aws-cloudfront-s3· Série : AWS Solutions Architect · Mot-clé SEO : CloudFront S3← Précédent : S3 versioning · → Suivant : VPC fondations · Hub : Démarrer avec AWS
Publish : HOLD
Prérequis
- Bucket S3 privé en
ca-central-1avecindex.html(site statique en mode privé, ou nouveau bucket) - Profil CLI
lab; BPA strict (pas de policy publique) - Accepter ~15–30 min de déploiement CloudFront (propagation edge)
- Budget d’alerte actif
export AWS_PROFILE=lab
export AWS_DEFAULT_REGION=ca-central-1
# PowerShell : $env:AWS_PROFILE='lab'; $env:AWS_DEFAULT_REGION='ca-central-1'
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
BUCKET="lab-${ACCOUNT_ID}-cdn"
Coût estimé lab (ca-central-1 bucket + CloudFront global, 2026) :
| Élément | Ordre de grandeur | Remarque |
|---|---|---|
| Stockage S3 | cents pour HTML Ko | Bucket privé |
| CloudFront Free Tier | 1 To / 10 M req (souvent) | Lab = négligeable |
| Distribution idle | faible hors FT | Supprimez après le lab |
| Invalidations | 1 000 paths/mois gratuits | / + /index.html OK |
Supprimez la distribution (disable → wait → delete) puis le bucket. Une distribution oubliée a un coût de base faible mais réel hors Free Tier.
Ce que nous allons construire
Utilisateur ──HTTPS──► CloudFront (PoP)
│ OAC
▼
S3 bucket privé (ca-central-1)
└── index.html (Block Public Access ON)
(Schéma — alt : « CloudFront OAC vers bucket S3 privé ».)
CloudFront est un CDN global : les PoP (points of presence) cachent les objets près des utilisateurs. L’origine reste votre bucket en ca-central-1. OAC remplace l’ancien OAI : signature SigV4 des requêtes origin, bucket policy conditionnée sur l’ARN de votre distribution.
Étape 1 — Pourquoi pas le website endpoint public
| Approche | HTTPS | Bucket | Cache edge | Recommandé prod |
|---|---|---|---|---|
| S3 website public | Non natif | Public | Non | Non |
| CloudFront + OAC | Oui | Privé | Oui | Oui |
| Ancien OAI | Oui | Privé | Oui | Legacy → migrer OAC |
OAC signe les requêtes origin ; la bucket policy n’autorise que le service CloudFront de votre distribution. Repère examen SA : le website endpoint (s3-website-…) ne supporte pas OAC de la même façon — utilisez l’endpoint REST bucket.s3.region.amazonaws.com comme origin. BPA reste all true : la policy OAC n’est pas une policy « publique ».
Étape 2 — Bucket privé + objet
aws s3api create-bucket --bucket "$BUCKET"
--create-bucket-configuration LocationConstraint=ca-central-1
--region ca-central-1
aws s3api put-public-access-block --bucket "$BUCKET"
--public-access-block-configuration
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
aws s3api put-bucket-ownership-controls --bucket "$BUCKET"
--ownership-controls 'Rules=[{ObjectOwnership=BucketOwnerEnforced}]'
mkdir -p /tmp/cdn && cat > /tmp/cdn/index.html <<'HTML'
<!DOCTYPE html><html lang="fr"><head><meta charset="utf-8" />
<title>CloudFront + S3 lab</title></head>
<body><h1>Servi via CloudFront OAC</h1>
<p>Bucket privé — ca-central-1</p></body></html>
HTML
aws s3 sync /tmp/cdn "s3://$BUCKET/" --region ca-central-1
Vérifiez qu’un curl direct sur l’URL objet S3 échoue (privé) — attendu. Taggez Project=lab pour Cost Explorer. BucketOwnerEnforced désactive les ACL objet : tout passe par IAM + bucket policy.
Étape 3 — Créer l’OAC
OAC_ID=$(aws cloudfront create-origin-access-control
--origin-access-control-config "
Name=lab-oac-${ACCOUNT_ID},
Description=Lab OAC S3,
SigningProtocol=sigv4,
SigningBehavior=always,
OriginAccessControlOriginType=s3"
--query 'OriginAccessControl.Id' --output text)
echo "$OAC_ID"
SigningBehavior=always force la signature de chaque requête origin. Conservez OAC_ID : il sera référencé dans la config de distribution. Un OAC peut être réutilisé par plusieurs distributions, mais en lab un OAC dédié simplifie le nettoyage.
Étape 4 — Distribution CloudFront (minimal)
Fichier dist-config.json (à générer dynamiquement) :
CALLER=$(date +%s)
cat > /tmp/dist-config.json <<EOF
{
"CallerReference": "lab-$CALLER",
"Comment": "lab-s3-oac",
"Enabled": true,
"DefaultRootObject": "index.html",
"Origins": {
"Quantity": 1,
"Items": [{
"Id": "s3-origin",
"DomainName": "${BUCKET}.s3.ca-central-1.amazonaws.com",
"S3OriginConfig": { "OriginAccessIdentity": "" },
"OriginAccessControlId": "${OAC_ID}"
}]
},
"DefaultCacheBehavior": {
"TargetOriginId": "s3-origin",
"ViewerProtocolPolicy": "redirect-to-https",
"AllowedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"],
"CachedMethods": { "Quantity": 2, "Items": ["GET", "HEAD"] }
},
"Compress": true,
"ForwardedValues": {
"QueryString": false,
"Cookies": { "Forward": "none" }
},
"MinTTL": 0,
"DefaultTTL": 3600,
"MaxTTL": 86400
},
"PriceClass": "PriceClass_100"
}
EOF
DIST_ID=$(aws cloudfront create-distribution
--distribution-config file:///tmp/dist-config.json
--query 'Distribution.Id' --output text)
DOMAIN=$(aws cloudfront get-distribution --id "$DIST_ID"
--query 'Distribution.DomainName' --output text)
echo "DIST_ID=$DIST_ID DOMAIN=$DOMAIN"
Si l’API refuse le combo
S3OriginConfigvide + OAC, utilisez la console : Origin domain = bucket, Origin access = Origin access control settings (recommended), create/select OAC. NotezDIST_IDet domain.
PriceClass_100 limite aux PoP Amérique/Europe (moins cher) — suffisant pour un lab. Attendez Status = Deployed (souvent 5–15+ min) :
aws cloudfront get-distribution --id "$DIST_ID"
--query 'Distribution.Status'
Sans bucket policy OAC, CloudFront peut se déployer mais renverra 403 à l’origine — normal jusqu’à l’étape 5.
Étape 5 — Bucket policy pour OAC
cat > /tmp/oac-bucket-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowCloudFrontOAC",
"Effect": "Allow",
"Principal": { "Service": "cloudfront.amazonaws.com" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::${BUCKET}/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::${ACCOUNT_ID}:distribution/${DIST_ID}"
}
}
}]
}
EOF
# BPA : BlockPublicPolicy peut rester true — cette policy n'est pas « publique »
aws s3api put-bucket-policy --bucket "$BUCKET"
--policy file:///tmp/oac-bucket-policy.json
La Condition AWS:SourceArn empêche toute autre distribution CloudFront (même dans votre compte) de lire le bucket. Oublier le DIST_ID correct est la cause n°1 des 403 lab.
Étape 6 — Tester HTTPS + invalidation
curl -sI "https://${DOMAIN}/" | head
curl -s "https://${DOMAIN}/" | head
Modifiez index.html, sync S3, puis invalidez (sinon cache TTL) :
aws s3 cp /tmp/cdn/index.html "s3://${BUCKET}/index.html" --region ca-central-1
aws cloudfront create-invalidation
--distribution-id "$DIST_ID"
--paths "/index.html" "/"
Alternative moins coûteuse en prod : versionner les assets (app.abc123.js) plutôt qu’invalider souvent. Les invalidations hors quota gratuit facturent par path ; un design « hash dans le nom » évite ce coût.
Cache behaviors utiles (aperçu SA)
- Default (*) : HTML avec TTL court ou managed policy « CachingDisabled » pour
index.htmlsi mises à jour fréquentes. /assets/*: TTL long + compress.- Viewer protocol :
redirect-to-https(lab) ouhttps-only.
Les managed cache policies / origin request policies remplacent progressivement ForwardedValues dans les configs modernes — la console les propose par défaut. Pour l’examen : sachez que TTL viewer ≠ TTL origin, et qu’une invalidation force le refresh edge sans changer l’objet S3.
Étape 7 — Custom domain (aperçu, hors lab minimal)
- Certificat ACM us-east-1 (requis CloudFront — même si le bucket est en
ca-central-1) - Alternate domain names (CNAMEs) sur la distribution
- Alias Route 53 A/AAAA → distribution
On le mentionne pour la certif ; pas obligatoire pour valider OAC. Piège classique : demander le certificat ACM dans la Region du bucket → CloudFront le refuse.
Étape 8 — Vérification
| Check | Attendu |
|---|---|
| S3 objet URL directe | AccessDenied |
https://DOMAIN/ |
200 + HTML |
| BPA | all true |
| Policy | Principal cloudfront.amazonaws.com + SourceArn distribution |
| Status | Deployed |
| Origin domain | bucket.s3.ca-central-1.amazonaws.com (REST, pas website) |
Checklist : OAC créé · dist Deployed · policy SourceArn exact · HTTPS redirect · invalidation OK après sync.
Nettoyage
# Désactiver puis supprimer la distribution (2 étapes, attente)
# Console souvent plus simple : Disable → wait → Delete
aws s3api delete-bucket-policy --bucket "$BUCKET"
aws s3 rm "s3://$BUCKET" --recursive --region ca-central-1
aws s3api delete-bucket --bucket "$BUCKET"
# OAC : delete-origin-access-control après suppression dist
Contrôle anti-facture :
aws cloudfront list-distributions
--query "DistributionList.Items[?Comment=='lab-s3-oac'].{Id:Id,Status:Status,Enabled:Enabled}"
aws s3api list-buckets --query "Buckets[?contains(Name,'-cdn')].Name"
Ne laissez pas une distribution idle : elle a un coût de base faible mais réel hors Free Tier.
Erreurs fréquentes
| Symptôme | Cause probable | Correction |
|---|---|---|
| 403 via CloudFront | Policy OAC / mauvais SourceArn | DIST_ID + account dans Condition |
| 403 après sync | Cache | Invalidation ou wait TTL |
| Origin domain wrong | Endpoint website au lieu de REST | bucket.s3.region.amazonaws.com |
| Create dist error | Config JSON legacy OAI+OAC | Console ou config OAC seule |
| ACM cert « invisible » | Certificat hors us-east-1 | Demander/importer en us-east-1 |
| BPA bloque put-policy | Confusion policy publique vs OAC | OAC n’est pas publique ; BlockPublicPolicy OK |
| Dist reste InProgress | Propagation normale | Attendre Deployed (5–20 min) |
| curl S3 direct 200 | Bucket encore public | BPA + retirer policy publique |
Lab bonus (5–8 min) — prouver le cache
- Notez l’en-tête
X-Cache/Agesurcurl -sI https://$DOMAIN/. - Relancez immédiatement : vous devez voir un HIT (ou équivalent) si TTL > 0.
- Changez le
<h1>dansindex.html, sync sans invalidation : l’ancien contenu peut rester jusqu’au TTL. - Créez l’invalidation
/index.html: après completion, le nouveau HTML apparaît.
Ce mini-lab ancre : edge cache ≠ S3, et invalidation = outil volontaire (coût en prod).
Quiz (5 questions)
1. OAC sert surtout à :
– A. Remplacer Route 53
– B. Laisser CloudFront lire un bucket privé de façon signée
– C. Ouvrir S3 en public automatiquement
2. Le certificat ACM pour un nom custom CloudFront se demande en :
– A. La Region du bucket uniquement
– B. us-east-1
– C. eu-west-3 seulement
3. Après un changement de index.html sur S3, le navigateur peut voir l’ancienne page à cause :
– A. Du VPC
– B. Du cache CloudFront (TTL)
– C. Du versioning EBS
4. L’origin S3 recommandé avec OAC utilise :
– A. L’endpoint website s3-website-…
– B. L’endpoint REST bucket.s3.region.amazonaws.com
– C. Un Elastic IP
5. PriceClass_100 limite surtout :
– A. La taille des objets S3
– B. Les PoP (régions edge) utilisés — coût réduit
– C. Le nombre de buckets
Réponses : 1‑B · 2‑B · 3‑B · 4‑B · 5‑B
Pour aller plus loin
- OAC
- Cache invalidation
- Price classes
- Prochains tutos : VPC · WAF
Maillage série AWS (P1)
| ← Précédent | S3 versioning |
| → Suivant | VPC sous-réseaux & IGW |
| Aussi | S3 site web (anti-pattern prod) · WAF · S3 sécurité |
Retour parcours AWS — hub de la série et leçons sœurs.



