D’abord un (tout petit) peu de théorie

Amazon Cloudfront, c’est quoi ?

Amazon Cloudfront est un CDN (Content Delivery Network), idéal pour servir des sites web statiques hébergés dans Amazon S3, type SPA (Single Page Application) et donc site codé par exemple en Angular, ReactJS ou encore VueJS. La doc du service est ici.

Un schéma vaut mille mots IMG-20250329075619770.png

L’utilisateur navigue sur le site site web depuis son ordinateur : il accède à celui-ci au travers de la distribution CloudFront et pas directement au bucket S3, afin de bénéficer des de CloudFront (mise en cache, DNS custos, WAF intégré, …).

En pratique !

Objectif

Le but de cet article est démontrer comment on peut servir plusieurs sites WEB, à partir d’une même distribution CloudFront (et donc d’une base url commune), comme illustré ci-dessous : IMG-20250329075645891.png

On déploie d’abord un seul site, avec terraform

  • Création d’un bucket S3
  • Upload de notre site web
  • Création de notre distribution CloudFront & configuration de celle-ci
  • On teste !

Création du bucket S3

Voici le code minimal pour créer le bucket S3 :

resource "aws_s3_bucket" "this" {
  bucket = "blog-site1-example"

  tags = {
    Name           = "blog-site1-example"
  }
}

Upload du site

Notre site web est composé d’un seul fichier index.html contenant le code suivant :

<h1>Accueil du Site 1 !</h1>

IMG-20250329075713392.png

Création de la distribution CloudFront

Encore une fois, il n’y a que le code minimal nécessaire pour créer la distribution.

resource "aws_cloudfront_distribution" "this" {
  enabled             = true
  default_root_object = "index.html"

  origin {
    domain_name = aws_s3_bucket.this.bucket_regional_domain_name
    origin_id = aws_s3_bucket.this.id
  }

  default_cache_behavior {
    allowed_methods = ["GET", "HEAD"]
    cached_methods = ["GET", "HEAD"]
    target_origin_id       = aws_s3_bucket.this.id
    viewer_protocol_policy = "allow-all"
    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
      locations = []
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

Permissions

On configure ensuite les permissions entre le bucket S3 et la distribution CloudFront pour que celle-ci puisse lire le site.

On ajoute une “Origin Access Control” côté CloudFront et on met à jour la bucket policy :

resource "aws_cloudfront_origin_access_control" "default" {
  name                              = "s3-access"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

resource "aws_s3_bucket_policy" "site1" {
  bucket = aws_s3_bucket.site1.id
  policy = jsonencode({
    "Version" : "2008-10-17",
    "Id" : "PolicyForCloudFrontPrivateContent",
    "Statement" : [
      {
        "Sid" : "AllowCloudFrontServicePrincipal",
        "Effect" : "Allow",
        "Principal" : {
          "Service" : "cloudfront.amazonaws.com"
        },
        "Action" : "s3:GetObject",
        "Resource" : "${aws_s3_bucket.site1.arn}/*",
        "Condition" : {
          "StringEquals" : {
            "AWS:SourceArn" : aws_cloudfront_distribution.this.arn
          }
        }
      }
    ]
  })
}

En utilisant “Distribution domain name”, on est désormais en mesure d’accéder au site. Les deux URLs suivantes sont valides : https://_distribution-id_.cloudfront.net et https://_distribution-id_.cloudfront.net/index.html

IMG-20250329075825450.png

Déployons maintenant le second site et essayons d’y accéder avec CloudFront

  • On crée un second bucket S3 > je ne vous remets pas le code de création, c’est le même
  • On met à jour la distribution CloudFront avec une second “origin” pointant vers le second bucket S3 et on ajoute un second “cache behavior” notamment pour préciser à CloudFront que toutes les requêtes arrivant sur la route /site2 doivent être redirigées vers cette seconde “origin”
resource "aws_cloudfront_distribution" "this" {
  enabled             = true
  default_root_object = "index.html"

  origin {
    domain_name              = aws_s3_bucket.site1.bucket_regional_domain_name
    origin_access_control_id = aws_cloudfront_origin_access_control.default.id
    origin_id                = aws_s3_bucket.site1.id
  }

  origin {
    domain_name              = aws_s3_bucket.site2.bucket_regional_domain_name
    origin_access_control_id = aws_cloudfront_origin_access_control.default.id
    origin_id                = aws_s3_bucket.site2.id
  }

  default_cache_behavior {
    allowed_methods = ["GET", "HEAD"]
    cached_methods = ["GET", "HEAD"]
    target_origin_id       = aws_s3_bucket.site1.id
    viewer_protocol_policy = "allow-all"
    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }
  }

  ordered_cache_behavior {
    path_pattern = "/site2/*"
    allowed_methods = ["GET", "HEAD"]
    cached_methods = ["GET", "HEAD"]
    target_origin_id       = aws_s3_bucket.site2.id
    viewer_protocol_policy = "allow-all"
    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
      locations = []
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

resource "aws_cloudfront_origin_access_control" "default" {
  name                              = "s3-access"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

On teste l’accès au deuxième site avec les URL suivantes : https://_distribution-id_.cloudfront.net/site2 ou https://_distribution-id_.cloudfront.net/site2/index.html ; et là, patatras !, “Access denied” : IMG-20250329075845666.png

Pourquoi ? Alors que tout est configuré de la même façon ?

Et bien en fait c’est très simple et c’est bien entendu lié à la façon dont fonctionne CloudFront et dont il construit sa redirection : il concatène l’URL des origin (= “origin domain”) avec le path de l’URL.

Dans notre exemple :

Comment on corrige alors ?

Hé bien, il suffit de déployer notre second site dans un sous dossier nommé site2 dans son bucket S3 : IMG-20250329075907669.png Et cette fois ci, on accède au second site avec succès : https://_distribution-id_.cloudfront.net/site2/index.html : IMG-20250329075937176.png

En conclusion

On sert désormais deux sites différents, à partir de la même distribution CloudFront.

Une fois qu’on a compris comment ça fonctionne, c’est vraiment simple, mais pas évident de prime abord.

J’espère que ces quelques minutes de lecture vous permettront d’éviter de longues minutes (heures !) de debug, de vérification des permissions et autres joyeusetés.