Hosting a Static Website on a Private S3 Bucket

I host this blog on AWS for pennies, but I refused to take the easy route of making the S3 bucket public.

S3 Website Hosting is a convenient default, but it exposes the origin directly to the internet. I wanted a setup that was secure by design, highly performant, and treated “static” hosting as a first-class engineering problem.

So I engineered it properly.

The stack:

  • Private S3 Bucket: No public access allowed.
  • CloudFront: The only entity allowed to read from S3 (via Origin Access Control).
  • CloudFront Functions: A lightweight edge function to handle “pretty URLs” (so /about serves /about/index.html) and redirects.
  • GitHub Actions + OIDC: Zero-trust CI/CD that assumes a temporary AWS role to deploy.
  • Terraform: The entire infrastructure is defined as code.

Here is how (and why) I built it.