CloudFront¶
CloudFront is the only public path to S3. Browsers, mobile clients, and any backend reading assets all hit a CloudFront distribution; S3 is never exposed directly. There is one distribution per environment, both defined in cloudfront.tf.
Distributions¶
| Environment | Public alias | Origin bucket |
|---|---|---|
| dev | assets-dev.tomoda.life |
tomoda-assets-dev |
| prod | assets.tomoda.life |
tomoda-assets-prod |
The alias is set on the distribution itself via the aliases argument and matched against the ACM certificate's domain_name. If the alias and cert ever diverge (e.g. someone renames the asset subdomain in s3.tf's local.assets_subdomain), CloudFront will refuse to serve TLS for the requested host.
Origin Access Control¶
CloudFront talks to S3 with an OAC, not the legacy OAI. Defined in cloudfront.tf:
resource "aws_cloudfront_origin_access_control" "default" {
name = "${local.bucket_name}-oac"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
signing_behavior = "always" means every origin request CloudFront issues is signed with SigV4 using the distribution's identity. S3's bucket policy (see S3) checks the AWS:SourceArn of the requesting distribution against an exact match, so an attacker who knew the bucket name but not the distribution ARN still cannot read objects.
Cache behaviour¶
The single default cache behaviour applies to all paths:
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 3600 # 1 hour
max_ttl = 86400 # 24 hours
forwarded_values {
query_string = false
cookies { forward = "none" }
}
}
Implications:
- HTTP is redirected to HTTPS at the edge — there is no plaintext path to any asset.
- Query strings and cookies are stripped before the origin lookup, so two requests to
…/avatar.jpg?v=1and…/avatar.jpg?v=2share a cache key. If the backend ever needs cache-busting, it must do so via a new filename, not a query parameter. - OPTIONS is allowed for CORS preflight but not cached.
- TTLs: objects without an explicit
Cache-Controlheader live at the edge for 1 hour; ones that shipCache-Control: max-age=…may live up to 24 hours.
Edge distribution¶
price_class = "PriceClass_All"
PriceClass_All enables every CloudFront edge location globally — North America, Europe, Asia, South America, Oceania, Africa, Middle East. Chosen because tomoda's user base spans Asia (primary), North America, and Europe. If usage consolidates and non-Asia egress becomes wasteful, drop to PriceClass_100 (US, Canada, Europe only).
TLS¶
viewer_certificate {
acm_certificate_arn = aws_acm_certificate.cert.arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
- The cert is the one provisioned by
aws_acm_certificate.cert— see ACM. It must be inus-east-1regardless of where the rest of the stack runs, because CloudFront is a global service whose control plane lives there. TLSv1.2_2021is the floor: TLS 1.2 with the modern cipher suite list, TLS 1.3 supported.sni-only— clients that do not send SNI cannot connect (any modern browser and HTTP client does).
Defaults worth knowing¶
default_root_object = "index.html"— a request tohttps://assets.tomoda.life/returnsindex.htmlfrom the bucket root, not a directory listing. There is currently noindex.htmluploaded for either environment; this is a no-op until something writes one.is_ipv6_enabled = true— the distribution accepts AAAA queries.geo_restriction.restriction_type = "none"— no country-level blocking. If that ever changes (regulatory, abuse), it goes here.
DNS¶
assets[-dev].tomoda.life resolves to <distribution-id>.cloudfront.net via a Cloudflare CNAME, configured in cloudflare.tf. Cloudflare does not proxy this record (proxied = false), so the edge cache is CloudFront alone — there is no double-CDN. See Cloudflare for the full DNS layering.
Operational notes¶
- Propagation: any change to a CloudFront distribution takes 3–5 minutes to roll out to all edges.
terraform applyblocks until the state isDeployed. - Cache invalidation: not automated. If a bad object needs immediate removal, issue a manual
aws cloudfront create-invalidation --paths '/path/to/object'.