MervCodes

Tech Reviews From A Programmer

AWS S3 and CloudFront for Static Site Hosting

1 min read

Static site hosting has become the default for blogs, documentation, marketing pages, and single-page applications. There are no servers to patch, scaling is effectively free, and the attack surface is tiny. Among the many ways to ship a static site, the combination of Amazon S3 and Amazon CloudFront remains one of the most reliable and cost-effective. S3 stores your files; CloudFront serves them from edge locations around the world with HTTPS and caching. This guide walks through how the two fit together and how to set them up correctly.

Why S3 and CloudFront Together

It is tempting to host directly out of an S3 bucket using its built-in website endpoint. That works for a quick prototype, but it has real limitations: no native HTTPS on custom domains, no global caching, and traffic served straight from one region. CloudFront solves all three.

  • HTTPS everywhere. CloudFront integrates with AWS Certificate Manager (ACM) so you get free TLS certificates for your custom domain.
  • Global performance. Content is cached at hundreds of edge locations, so a visitor in Tokyo and a visitor in Frankfurt both get low-latency responses.
  • Lower cost and load. Cache hits never touch S3, which reduces request and data-transfer charges.
  • Security. You can lock the bucket down completely and only allow CloudFront to read from it.

The mental model is simple: S3 is the origin (the source of truth), CloudFront is the delivery network. Visitors never talk to S3 directly.

Setting Up the S3 Bucket

Create a bucket to hold your site files. The bucket name no longer needs to match your domain when you front it with CloudFront, so pick something descriptive like mysite-prod-origin.

A few key decisions:

  1. Block all public access. This is the modern best practice. Instead of making the bucket public, you grant CloudFront access through an Origin Access Control (OAC). OAC replaces the older Origin Access Identity (OAI) and is the recommended approach today.
  2. Disable static website hosting on the bucket when using OAC with the REST endpoint. You only need the plain S3 origin. (If you rely on S3's redirect rules, that is the one case where the website endpoint still matters.)
  3. Enable versioning if you want easy rollback of accidentally overwritten files.

Upload your built site with the AWS CLI:

aws s3 sync ./dist s3://mysite-prod-origin --delete

The --delete flag removes files in the bucket that no longer exist locally, keeping the origin clean.

Configuring CloudFront

Create a CloudFront distribution and point its origin at the S3 bucket. The important settings:

  • Origin access: choose "Origin access control settings" and create an OAC. CloudFront will generate a bucket policy snippet for you to paste into the S3 bucket — this is what grants the distribution permission to read objects.
  • Viewer protocol policy: set to "Redirect HTTP to HTTPS" so every visitor lands on a secure connection.
  • Default root object: set to index.html so requests to / resolve correctly.
  • Compression: enable automatic object compression (Gzip/Brotli) to shrink text assets.

Here is a representative bucket policy that allows only your distribution to read objects:

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

Custom Domains and HTTPS

To serve www.example.com instead of the default *.cloudfront.net URL:

  1. Request an ACM certificate in us-east-1. This is a hard requirement — CloudFront only reads certificates from the North Virginia region, regardless of where your bucket lives.
  2. Validate the certificate via DNS by adding the CNAME records ACM provides.
  3. Add your domain as an alternate domain name (CNAME) in the CloudFront distribution and attach the certificate.
  4. Point your DNS at CloudFront. If you use Route 53, create an A record as an Alias to the distribution rather than a plain CNAME, which lets you alias the apex domain (example.com).

Handling SPA Routing and Clean URLs

Single-page applications built with React, Vue, or similar frameworks use client-side routing. When a user refreshes /dashboard, S3 returns a 404 because no such object exists. Fix this with a CloudFront custom error response: map HTTP 403 and 404 to return /index.html with a 200 status code. The app then boots and resolves the route itself.

For static site generators that produce clean URLs (e.g., /about/ rather than /about.html), use a CloudFront Function on the viewer request event to rewrite directory paths to their index.html file. CloudFront Functions are lightweight, sub-millisecond, and far cheaper than Lambda@Edge for simple URL manipulation.

Cache Invalidation and Deployments

Caching is what makes CloudFront fast, but it also means new deploys may not appear immediately. There are two main strategies:

  • Fingerprinted filenames. Most build tools emit hashed names like app.7f3a9.js. These can be cached forever (Cache-Control: max-age=31536000, immutable) because a new build produces a new filename.
  • Invalidate index.html (and other unhashed files) on deploy. Keep HTML on a short TTL and explicitly invalidate it:
aws cloudfront create-invalidation \
  --distribution-id EXXXXXXXXXX \
  --paths "/index.html" "/"

AWS gives you 1,000 free invalidation paths per month, so a wildcard /* invalidation on every deploy is fine for low-frequency releases, but path-specific invalidations are cheaper at scale.

Cost Expectations

For most small-to-medium sites, this setup costs a few dollars a month or less. S3 storage is pennies per gigabyte, and CloudFront's free tier includes a generous allotment of data transfer and requests. Your main charges are outbound data transfer from CloudFront and request volume — both of which scale gracefully. There are no idle server costs because there is no server.

Practical Tips

  • Automate deployments with a CI/CD pipeline (GitHub Actions, CodePipeline) that runs s3 sync and then a CloudFront invalidation.
  • Set sensible Cache-Control headers at upload time rather than relying solely on CloudFront TTLs, so the policy travels with each object.
  • Enable logging or use CloudWatch to understand cache hit ratios; a low hit ratio usually means misconfigured headers.
  • Use Origin Access Control, not public buckets. A public bucket is the most common security mistake in static hosting.
  • Consider a staging distribution so you can validate cache and routing behavior before touching production.

FAQ

Do I still need S3 if I use CloudFront? Yes. CloudFront is a cache and delivery layer, not storage. It needs an origin to pull from, and S3 is the natural choice for static files.

Can I host directly from S3 without CloudFront? You can, using the S3 static website endpoint, but you lose HTTPS on custom domains, global caching, and fine-grained security. For anything public-facing, add CloudFront.

Why must my ACM certificate be in us-east-1? CloudFront is a global service whose certificate management is centralized in the North Virginia region. Certificates in any other region simply won't appear as options when configuring the distribution.

My site updates aren't showing up. What's wrong? CloudFront is serving a cached copy. Invalidate the affected paths (especially index.html) or use fingerprinted filenames so new builds produce new URLs.

OAC or OAI — which should I use? Origin Access Control (OAC) is the current recommendation. It supports newer features like SSE-KMS encryption and all AWS regions. OAI still works but is considered legacy.

How do I handle a single-page app's routing? Add a CloudFront custom error response that returns /index.html with a 200 status for 403 and 404 errors, letting your client-side router take over.

Conclusion

S3 and CloudFront give you a production-grade static hosting platform that is fast, secure, and inexpensive. The pattern is durable: store files in a locked-down S3 bucket, serve them through CloudFront with OAC and HTTPS, handle routing with custom error responses or CloudFront Functions, and invalidate the cache on deploy. Once the pipeline is automated, shipping a new version of your site is a single command — and your visitors get edge-speed delivery anywhere in the world.

Sources

Related Articles

AI Code Review: The Complete Guide for Engineering Teams (2026)

A definitive, practical guide to AI code review in 2026 — how it works, where it helps and where it doesn't, how to roll it out, prompt and config patterns, security trade-offs, and the metrics that prove it's working.

AI Embeddings: Practical Applications for Developers

TanStack Query Guide for React: Server State Made Simple