Ravindra BagaleCourses & study guides मराठी Track your progress

How to Host a Static Website on S3 with CloudFront, HTTPS (ACM) and Route 53

In S3 Bucket Policy Examples we learned to lock buckets down. Now we open one carefully to the world: a static website on S3, then the production way with CloudFront, Origin Access Control (OAC), a free ACM certificate for HTTPS and a Route 53 alias for your domain.

What you'll learn in this chapter

  • Why a static site does not need a server
  • S3 website hosting: index, error page, website endpoint
  • Why CloudFront + OAC beats a public bucket
  • HTTPS with ACM (us-east-1) and a Route 53 alias
  • Deploying from React, Angular, Vue, Hugo

1. A bakery story

A bakery wants a simple website: menu, photos, phone number. Renting a server means paying every hour, patching Linux and worrying about traffic on Diwali. But the pages never change per visitor. They are just files.

Files belong in S3. Put HTML, CSS, JS and images in a bucket and S3 serves them. Cost: a few rupees a month.

2. S3 static website hosting

Why do we need this?

A plain bucket returns files by exact key. Websites need "/" to show index.html and a friendly 404 page. Website hosting adds that.

What is it?

SettingValueWhy
Static website hostingEnableTurns on the website endpoint
Index documentindex.htmlShown for / and for every folder
Error documenterror.htmlShown on 404
Block Public AccessOff (this bucket only)The website endpoint has no signing; it needs public read
Bucket policyPublic GetObject (example 1 of the previous chapter)Lets visitors read

Endpoint for ap-south-1: http://bucket.s3-website.ap-south-1.amazonaws.com. Some older Regions use a dash, like s3-website-us-east-1. The website endpoint is HTTP only. That is the main reason we add CloudFront.

3. CloudFront with Origin Access Control

Why do we need this?

Without CloudFront: no HTTPS, the bucket must be public, and a visitor in London waits for Mumbai. CloudFront fixes all three.

What is it?

CloudFront is AWS's CDN with hundreds of edge locations that cache your files near users. OAC lets only your distribution read the bucket, so the bucket stays fully private with Block Public Access on.

Production static siteVisitorhttps://www.bakery.inRoute 53alias A recordCloudFrontACM cert, cacheOACsigned requestS3 bucketprivate, REST endpoint
The bucket is never public. Only CloudFront can read it.
PointS3 website endpointCloudFront + OAC
HTTPSNoYes, ACM free cert
Bucket publicYesNo
Speed worldwideOne RegionEdge cache
Origin to choose-S3 REST endpoint (bucket.s3.ap-south-1.amazonaws.com), not the website endpoint

Ravindra Bagale's Tip

OAC only works with the S3 REST endpoint. With OAC, set "Default root object" = index.html in CloudFront. Subfolder index pages need a CloudFront Function, or build tools that output folder/index.html links.

4. HTTPS with ACM and Route 53

Why do we need this?

Browsers mark HTTP sites "Not secure". ACM gives free public certificates that renew automatically.

What is it?

  • The certificate for CloudFront must be requested in us-east-1 (N. Virginia), whatever Region your bucket is in.
  • Validate by DNS: ACM gives a CNAME; with Route 53 one click creates it.
  • Add the domain under "Alternate domain names (CNAMEs)" in CloudFront.
  • In Route 53 create an A record, Alias = yes, target the distribution. Alias works at the root (bakery.in) where CNAME cannot, and alias queries to AWS targets are free.

5. Deploy from any stack

StackBuildUpload
React (Vite)npm run build → dist/aws s3 sync dist/ s3://b --delete
Angularng build → dist/app/browseraws s3 sync dist/app/browser s3://b --delete
Vuenpm run build → dist/aws s3 sync dist/ s3://b --delete
Next.js (export)output:"export", next build → out/aws s3 sync out/ s3://b --delete
Hugohugo → public/aws s3 sync public/ s3://b --delete
Plain HTML-aws s3 sync ./site s3://b

After each deploy: aws cloudfront create-invalidation --distribution-id E123 --paths "/index.html". Why: CloudFront caches; invalidation fetches the new file. Hashed asset names (app.3f9a.js) avoid invalidating everything.

SPA routing (React Router): in CloudFront › Error pages, map 403 and 404 to /index.html with response 200.

6. Lab: part A website endpoint, part B CloudFront + OAC + HTTPS

How to do it (step by step)

Part A: S3 website

  1. Create files: echo "<h1>Bakery</h1>" > index.html; echo "<h1>Not found</h1>" > error.html.
  2. Create bucket yourname-bakery-site in ap-south-1. Uncheck Block all public access, tick the acknowledgement.
  3. aws s3 cp index.html s3://yourname-bakery-site/; aws s3 cp error.html s3://yourname-bakery-site/.
  4. Properties › Static website hosting › Edit › Enable, Index = index.html, Error = error.html › Save.
  5. Permissions › Bucket policy: public GetObject on arn:aws:s3:::yourname-bakery-site/*.
  6. curl http://yourname-bakery-site.s3-website.ap-south-1.amazonaws.com/. Expected: <h1>Bakery</h1>. /nope gives Not found.

Part B: make it private behind CloudFront

  1. Delete the bucket policy, disable website hosting, turn Block all public access back on.
  2. (If you own a domain) ACM, Region us-east-1 › Request public certificate › www.yourdomain.com › DNS validation › Create records in Route 53. Wait for "Issued".
  3. CloudFront › Create distribution › Origin domain = yourname-bakery-site.s3.ap-south-1.amazonaws.com (not the website one).
  4. Origin access = Origin access control settings › Create new OAC (Sign requests).
  5. Viewer protocol policy = Redirect HTTP to HTTPS. Default root object = index.html. WAF = do not enable (lab).
  6. (Domain) Alternate domain name = www.yourdomain.com, Custom SSL certificate = the ACM cert. Create.
  7. Copy the policy CloudFront shows and paste it into the bucket policy. It allows cloudfront.amazonaws.com with AWS:SourceArn = your distribution.
  8. Wait for Deployed. curl -I https://dxxxx.cloudfront.net/. Expected: HTTP/2 200, x-cache header.
  9. Verify private: curl -I https://yourname-bakery-site.s3.ap-south-1.amazonaws.com/index.html. Expected: 403.
  10. (Domain) Route 53 › hosted zone › Create record › www, A, Alias on, Alias to CloudFront distribution. Open https://www.yourdomain.com.

Cleanup

  1. Delete the Route 53 record. CloudFront › Disable, wait, then Delete.
  2. aws s3 rb s3://yourname-bakery-site --force. Delete the ACM cert if unused.

7. Interview questions

Q. Does the S3 website endpoint support HTTPS?

No, HTTP only. Use CloudFront for HTTPS.

Q. What is OAC?

Origin Access Control: CloudFront signs requests to S3 so only the distribution can read a private bucket. It replaces the older OAI.

Q. Which Region for a CloudFront ACM certificate?

us-east-1, always.

Q. Why alias and not CNAME for bakery.in?

A CNAME is not allowed at the zone apex; an alias A record is, and AWS alias queries are free.

Q. Users see the old page after deploy. Why?

CloudFront cache. Create an invalidation or use hashed file names.

Try at home

  1. Deploy a React build to S3 + CloudFront and fix deep links with the 403/404 → index.html rule.
  2. Compare curl -I timings from the website endpoint and CloudFront.

In short

  1. Static sites are files; S3 serves files.
  2. Website endpoint = HTTP only and needs a public bucket.
  3. CloudFront + OAC keeps the bucket private and adds HTTPS and caching.
  4. ACM cert for CloudFront lives in us-east-1.
  5. Route 53 alias A record points the domain at CloudFront.

Practice Tasks

About this scenario

Sahyadri Clinic is a healthcare business in Satara. Dr. Anil Deshmukh looks after its IT and cloud work. They count money in INR and run their AWS resources in the ap-south-1 Region. In the tasks below you do real jobs for them using what this lesson teaches. All names, account IDs, IP addresses and numbers are made up for practice.

0 / 12

Easy List distributions

Why: Find the distribution ID and domain name.

What to do: List all CloudFront distributions.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Easy Read-only access to one bucket

Why: Least privilege: give only the actions and the bucket the job needs.

What to do: Write an identity policy that lets the user read objects (s3:GetObject) only in bucket sahyadriclinic-data.

How:
  1. Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
  2. In each statement write Effect, Action and Resource.
  3. Give only what the task needs (least privilege).
  4. Use double quotes and commas correctly. Click Check answer.

Medium Clear the CloudFront cache

Why: After a deploy, invalidate so visitors get the new files now, not after the TTL.

What to do: Invalidate all paths (/*) on distribution E2QWRUHAPOMQZL.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Bucket policy for a public website

Why: A static website bucket must let everyone read its files.

What to do: Write a bucket policy that lets anyone (*) do s3:GetObject on every object in sahyadriclinic-site.

How:
  1. Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
  2. In each statement write Effect, Action and Resource.
  3. Give only what the task needs (least privilege).
  4. Use double quotes and commas correctly. Click Check answer.

Medium Sync a website folder to S3

Why: sync uploads only new or changed files and can remove deleted ones.

What to do: Sync the local folder ./site to s3://sahyadriclinic-web/ and delete files in S3 that are no longer in ./site.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Turn a bucket into a website

Why: Static website hosting serves index.html and an error page straight from S3.

What to do: Enable static website hosting on s3://sahyadriclinic-web with index.html as the index page and error.html as the error page.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium List and read one bucket

Why: ListBucket works on the bucket ARN; GetObject works on object ARNs.

What to do: Allow s3:ListBucket on bucket sahyadriclinic-data and s3:GetObject on all its objects. Nothing else.

How:
  1. Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
  2. In each statement write Effect, Action and Resource.
  3. Give only what the task needs (least privilege).
  4. Use double quotes and commas correctly. Click Check answer.

Hard Create a distribution from a config file

Why: The full config (origins, behaviors, certificate) is easiest to keep in JSON.

What to do: Create a CloudFront distribution from dist.json.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Bucket policy for CloudFront OAC

Why: With OAC, only your CloudFront distribution may read the private bucket.

What to do: Allow the service cloudfront.amazonaws.com to s3:GetObject on sahyadriclinic-site/*, only when aws:SourceArn equals arn:aws:cloudfront::102103063049:distribution/E2QWRUHAPOMQZL.

How:
  1. Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
  2. In each statement write Effect, Action and Resource.
  3. Give only what the task needs (least privilege).
  4. Use double quotes and commas correctly. Click Check answer.

Hard Request a free certificate for CloudFront

Why: CloudFront only uses ACM certificates from us-east-1. DNS validation renews automatically.

What to do: Request a certificate for sahyadriclinic.in and www.sahyadriclinic.in with DNS validation, in us-east-1.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Invalidate two paths

Why: Invalidating only what changed is cheaper and faster.

What to do: On E2QWRUHAPOMQZL, invalidate /index.html and /css/site.css.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Create an Origin Access Control

Why: OAC lets only CloudFront read your private S3 bucket.

What to do: Create an origin access control from oac.json.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Revision, comparison and interview cards

Revise this lesson in a few minutes. Save the cards on your phone or download them as PNG files. The QR code on each card opens this lesson.

Revision card: S3 static website. Static sites are just files. S3 serves files. Enable hosting, index.html, error.html. Website endpoint is HTTP only and needs public read. Endpoint format in Mumbai.
Revision card: S3 static website · Download PNG
Revision card: CloudFront with OAC. CloudFront caches your site at edge locations and adds HTTPS. OAC lets only your distribution read the bucket. Use the S3 REST endpoint as origin, not the website one. Bucket stays private with Block Public Access on.
Revision card: CloudFront with OAC · Download PNG
Revision card: ACM HTTPS and Route 53 alias. Request the CloudFront certificate in us-east-1. Validate by DNS; ACM renews it for free. Route 53 alias A record points the domain to CloudFront. Alias works at the root domain where CNAME cannot.
Revision card: ACM HTTPS and Route 53 alias · Download PNG
Comparison: S3 website endpoint vs CloudFront + OAC. S3 website endpoint: The bucket serves the site itself from one Region. CloudFront + OAC: A CDN serves the site from edge caches and reads a private bucket.
Comparison: S3 website endpoint vs CloudFront + OAC · Download PNG
Comparison: Route 53 alias record vs CNAME record. Route 53 alias record: An AWS extension that points a name to an AWS resource. CNAME record: A standard DNS pointer from one name to another.
Comparison: Route 53 alias record vs CNAME record · Download PNG
Interview card: How do you serve a private bucket over HTTPS on your domain?. CloudFront with OAC, origin = S3 REST endpoint. ACM certificate in us-east-1, added as the alternate domain. Route 53 alias A record to the distribution.
Interview card: How do you serve a private bucket over HTTPS on your domain? · Download PNG

More cards: Ravi Sir's Cards.