10.3
Static Website Hosting and CORS
Serving a site from a bucket, the 403 trap, and cross-origin requests with preflight.
18 min Reading + animations
Tip: tap or hover any dotted-underlined term to see what it means.
By the end of this lesson you will be able to
- Explain what a static website is, why S3 can host one without any server, and when it is the cheapest correct answer.
- Turn a bucket into a website: enable hosting, set the index and error documents, unblock public access and write the public-read bucket policy.
- Read both website endpoint formats, and compare the website endpoint with the REST endpoint: HTTP only, public reads only, HTML errors, index documents and redirects.
- Diagnose the 403 trap and every other common error (404, 302, 400, HTTPS failures, SSE-KMS, Requester Pays) from the status code alone.
- Configure the three kinds of redirect, a custom domain with Route 53, and HTTPS through CloudFront or Amplify Hosting.
- Explain origins, the same-origin policy and CORS, predict when a browser sends a preflight, and write a CORS configuration that allows exactly what a page needs.
#A website with no server
#What “static” means
A static website is a set of files that are sent to the browser exactly as they are stored: HTML pages, CSS style sheets, JavaScript files, images, fonts, .pdf downloads. Nothing is computed on the server for each visitor. A dynamic site runs code on a server for every request (PHP, Node.js, Python, Java…) to build the page, read a database or check a login.
“Static” does not mean “boring”. A modern single-page application (SPA) built with React, Angular or Vue compiles to static files. All the interactivity runs as JavaScript in the browser, which can call APIs elsewhere (API Gateway, Lambda, an ALB) for dynamic data. The files themselves are static, and S3 can serve them.
What S3 cannot do is run code on the server side: no PHP, no Node.js, no Python, no server-side rendering, no database queries, no form handler. If a page has to be built per request, that part needs EC2, containers or Lambda (chapter 15).
#What it costs
Website hosting is a setting, not a separate product, and has no fee of its own. You pay the normal S3 prices: storage per GB-month, a small charge per GET request, and data transfer out to the internet per GB (lesson 10.1 lists the components). For a busy site, CloudFront in front usually makes it cheaper and faster, because transfer from S3 to CloudFront is free and the edge caches serve repeat visitors.
#Turning a bucket into a website
Enable static website hosting
In the bucket’s Properties, under Static website hosting, choose Enable and Host a static website. (The other choice, “Redirect requests for an object”, makes the whole bucket a redirect; see “Redirects” below.)
Name the index document, and optionally the error document
The index document (usually
index.html) is required: it is what S3 returns for the root and for any path ending in a slash. The error document (for example404.html) is optional. Both names are case-sensitive and must match the uploaded objects exactly. Then upload the site’s files.Turn off Block Public Access
New buckets have all four Block Public Access settings on (lesson 10.2), and so may your account. Clear them for this bucket, and at the account level if they are on there: S3 applies the most restrictive combination of the two.
Add a bucket policy that allows public reads
Grant everyone
s3:GetObjecton the bucket’s objects (note the/*):{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::mishti-menu-example/*" } ] }
Notice what the policy does not grant: s3:ListBucket. Visitors can read any object whose name they know, but they cannot list the bucket. That is what you want.
The notes put the requirement in one line: the bucket must allow public read access through the bucket policy. Two more details matter today. Since April 2023, new buckets have ACLs disabled (Object Ownership = bucket owner enforced), so the bucket policy is the only way to make objects public, and it covers every object no matter who uploaded it. On an old bucket with ACLs still enabled, objects uploaded by another account are owned by that account and the bucket policy does not apply to them; they need a public-read ACL, or better, switch the bucket to bucket owner enforced.
#The website endpoint
A website bucket answers on a website endpoint, which is different from the normal S3 API (REST) endpoint. Depending on the Region, it has one of two formats:
| Format | Example | Regions |
|---|---|---|
s3-website- dash Region | http://mishti-menu-example.s3-website-us-east-1.amazonaws.com | The older Regions: us-east-1, us-west-1, us-west-2, eu-west-1, ap-southeast-1, ap-southeast-2, ap-northeast-1, sa-east-1 (and GovCloud US-West) |
s3-website dot Region | http://mishti-menu-example.s3-website.ap-south-1.amazonaws.com | Every other Region, including all newer ones (ap-south-1, eu-central-1, us-east-2…) |
Both start with http://, never https://. The bucket name comes first, so the endpoint is unique worldwide, just like the bucket name. You do not need to memorise which Region uses which separator; the exam only expects you to recognise both shapes as S3 website endpoints, and the console shows the right one under Static website hosting.
One request to the website endpoint
A visitor opens the Mishti Ghor menu site. The browser talks plain HTTP to the bucket’s website endpoint, which turns the path into an object key, checks that the object is public, and answers with HTML.
Scenario
Hosting on, Block Public Access off, a public-read policy: the normal case.
Bucket settings
| Block Public Access | Bucket policy | Index document | Error document |
|---|---|---|---|
| Off | Public read on /* | index.html | 404.html |
The visitor types the website endpoint, mishti-menu-example.s3-website.ap-south-1.amazonaws.com, with no path. The browser sends GET / over plain HTTP on port 80.
#Index documents and folders
S3 has no real folders (lesson 10.1), but the website endpoint makes keys with slashes behave like them:
/and/menu/(a trailing slash) return the index document of that “folder”:index.htmlandmenu/index.html. Every folder needs its own index document, with the same name./menu(no trailing slash): S3 first looks for an object whose key is exactlymenu. If there is none butmenu/index.htmlexists, it answers 302 Found and redirects to/menu/. If neither exists, 404.- In a versioned bucket, only the newest version of
index.htmlis served.
#Error documents
When something goes wrong, the website endpoint returns an HTML page (the REST endpoint returns XML). With an error document configured, S3 returns your page for every 4xx error, with the real status code; 5xx errors always get S3’s own page. The error document must be readable by the public too, or visitors get S3’s plain page.
#The 403 trap, and other errors
#Why you get 403 Forbidden
The slides give the classic advice: if you get 403 Forbidden, make sure the bucket policy allows public reads. In real life there are several more causes. They all come down to one fact: website visitors are anonymous, so an object is served only if anonymous reads are allowed, and nothing else gets in the way.
| Cause | Why it fails | Fix |
|---|---|---|
| Block Public Access on (bucket or account) | A public policy cannot be saved, and one saved earlier is ignored | Turn it off at both levels for this bucket’s needs |
| No bucket policy | Objects are private by default; turning off Block Public Access alone makes nothing public | Add the public-read policy |
Policy Resource is arn:aws:s3:::bucket without /* | That names the bucket, not its objects | Use arn:aws:s3:::bucket/* |
| Objects owned by another account (old bucket with ACLs enabled) | Bucket policies apply only to objects the bucket owner owns | Bucket owner enforced, or a public-read ACL on those objects |
| Objects encrypted with SSE-KMS | Decrypting needs a KMS call by an authorised identity; an anonymous visitor has none | SSE-S3 for public content, or CloudFront with OAC and a key policy (lesson 10.8) |
| Requester Pays turned on | An anonymous visitor cannot be billed; website endpoints never work with it | Turn it off (lesson 10.7) |
An explicit Deny in the bucket policy (for example aws:SourceIp) | Deny always wins (lesson 2.2) | Adjust the condition |
#404 and the other status codes
| Status | When the website endpoint returns it |
|---|---|
| 200 OK | The object (or the index document) exists and is publicly readable |
| 301 Moved Permanently | A redirect rule, a “redirect all requests” bucket, or an object with redirect metadata |
| 302 Found | A folder was requested without its trailing slash |
| 304 Not Modified | The browser’s cached copy is still current (conditional request) |
| 400 Bad Request | The request reached the wrong Region’s website endpoint for this bucket |
| 403 Forbidden | The object exists but is not publicly readable (see the table above) |
| 404 Not Found | The key does not exist, the index document is missing, the bucket does not exist, or the bucket is not configured as a website |
| 405 Method Not Allowed | Anything other than GET or HEAD |
| 500 / 503 | An internal error, or “slow down”: reduce the request rate (lesson 10.6) |
There is a side effect worth knowing: because a public website answers 404 for missing keys and 403 for private ones, anyone can tell which keys exist. If that matters, do not enable website hosting; keep the bucket private behind CloudFront.
Try to reproduce each cause yourself. Start with the default settings, as a new bucket has them, and work your way to a 200:
Website tester: why 403?
Set up the bucket, pick an endpoint and a path, and send an anonymous request, the way a visitor’s browser would. Try to reproduce every 403 and 404 in this lesson.
Bucket mishti-menu-example
Objects: index.html, about.html, 404.html, menu/index.html
Bucket policy
Encryption of the objects
Path
Press “Send request” to see the answer.
#Website endpoint or REST endpoint
A website bucket still has its normal REST endpoint, https://mishti-menu-example.s3.ap-south-1.amazonaws.com, and the two behave very differently:
| REST endpoint | Website endpoint | |
|---|---|---|
| Built for | Programs: the S3 API, SDKs, the CLI | Browsers |
| Access control | Public and private content (signed requests, IAM) | Publicly readable content only |
| Requests | Every bucket and object operation | GET and HEAD on objects only |
GET / | Lists the bucket (if allowed) | Returns the index document |
Folders (/menu/) | Just a key; no index document | menu/index.html |
| Errors | XML | HTML, optionally your error document |
| Redirects | None (an object with redirect metadata is returned as is) | Bucket-level and object-level redirects |
| HTTPS | Yes | No, HTTP only |
| Access points, Requester Pays | Supported | Not supported |
Same bucket, two endpoints
A bucket with website hosting on answers on two different hostnames. The REST endpoint is the normal S3 API; the website endpoint is a small web server for browsers. Same objects, different behaviour.
Scenario
On the website endpoint, the root means the index document.
Self-checkQuestion
A developer opens https://mishti-menu-example.s3.ap-south-1.amazonaws.com/ in a browser, expecting the home page. The bucket has website hosting and a public-read policy. What does the browser show?
#Redirects
The website endpoint can send visitors elsewhere in three ways:
- Redirect all requests to another host. Set a whole bucket’s hosting option to “Redirect requests for an object”, with a host name (and optionally http or https). The classic use is
www.example.com→example.com: two buckets named after the two names, the content in one, the other empty and redirecting, with the path kept. - Routing rules in the website configuration (JSON in the console, up to 50 rules). A rule has a condition, a key prefix (
KeyPrefixEquals) and/or an error code (HttpErrorCodeReturnedEquals), and a redirect: a new host, protocol, replacement prefix (ReplaceKeyPrefixWith) or replacement key (ReplaceKeyWith), and a redirect code. Use it when a folder is renamed, a section is retired, or 404s should go to a server that can handle them. - Object redirect: set the object’s
x-amz-website-redirect-locationmetadata to another key (with a leading/) or a full URL. The website endpoint answers 301; the REST endpoint ignores it and returns the object. No 50-rule limit, so it suits many individually moved pages.
[
{
"Condition": { "KeyPrefixEquals": "docs/" },
"Redirect": { "ReplaceKeyPrefixWith": "documents/" }
},
{
"Condition": { "HttpErrorCodeReturnedEquals": "404" },
"Redirect": { "HostName": "app.example.com", "ReplaceKeyPrefixWith": "report-404/" }
}
]
Three kinds of redirect
A website bucket can redirect everything to another host, redirect by rule (a key prefix or an error code), or redirect one object through its metadata.
Scenario
To serve both example.com and www.example.com, you create two buckets named exactly like the two names. The content lives in example.com; the www bucket is empty and set to “redirect requests for an object” to the host example.com.
#Your own domain, and HTTPS
#A custom domain with Route 53
Visitors should type example.com, not a long S3 hostname. The notes say a custom domain needs CloudFront; that is only half true. A custom domain alone does not need CloudFront:
- Name the bucket exactly like the domain:
example.com(andwww.example.comfor the www redirect bucket). - In Route 53, create an Alias A record for
example.comthat targets the S3 website endpoint (lesson 8.2). A subdomain can also use a CNAME to the website endpoint at any DNS provider.
This works because S3 picks the bucket from the Host header of the HTTP request. It also means: if the bucket name example.com is taken by someone else, the method is impossible, and the bucket does not appear as an Alias target unless the names match.
#HTTPS needs CloudFront (or Amplify Hosting)
The website endpoint has no HTTPS at all, and you cannot attach a certificate to it. Today browsers label HTTP sites “Not secure”, and many features (service workers, geolocation, the clipboard) need HTTPS. The options:
- CloudFront with the website endpoint as a custom origin. CloudFront serves HTTPS with a free ACM certificate (requested in us-east-1 for CloudFront) for
example.com; the Route 53 Alias points at the distribution. You keep index documents in every folder and the redirects, but the bucket stays public and the CloudFront-to-S3 hop is HTTP. - CloudFront with the REST endpoint and Origin Access Control (OAC). Website hosting off, Block Public Access on, the bucket policy allows only that distribution. HTTPS end to end, nobody can bypass CloudFront, and SSE-KMS works. You set a default root object for
/, and handle sub-folder index pages and redirects with CloudFront Functions. This is what AWS recommends for most new sites, and it is covered in depth in chapter 11 (CloudFront basics). - AWS Amplify Hosting (side note). The S3 documentation now recommends it as the simplest way to put HTTPS on a site stored in S3: you pick the bucket and prefix, and Amplify deploys the files to a CloudFront-backed CDN with an HTTPS URL and custom domains. It is not on the SAA-C03 exam; there the answer to “HTTPS for an S3 static website” is CloudFront.
Your own domain, and HTTPS
Route 53 can point example.com straight at a website bucket over HTTP. For HTTPS, or to keep the bucket private, CloudFront sits in front.
Scenario
A bucket named exactly example.com has website hosting on. In Route 53, an Alias A record for example.com targets that bucket’s website endpoint (lesson 8.2). No CloudFront needed for a custom domain.
#CORS: when the page and the files live apart
#Origins and the same-origin policy
An origin is the triple scheme + host + port of a URL:
https://www.example.comhas schemehttps, hostwww.example.comand the implied port 443 (80 forhttp).https://www.example.com/app1andhttps://www.example.com/app2are the same origin: the path does not count.https://www.example.comandhttps://other.example.comare different origins (different host), as arehttp://www.example.com(different scheme) andhttps://www.example.com:8443(different port).
Browsers follow the same-origin policy: a script loaded from one origin may send requests to another origin, but may not read the response unless the other origin explicitly allows it. Without this rule, any website you visit could use your logged-in session to read your bank’s pages. CORS (Cross-Origin Resource Sharing) is how the other origin says “this origin may read me”: with response headers such as Access-Control-Allow-Origin.
Three points people often miss:
- CORS is enforced by the browser, not by S3. The same file opens fine in a new tab, with curl, or from a server. S3 only answers the browser’s questions.
- It applies to cross-origin reads made by scripts (
fetch,XMLHttpRequest), to web fonts loaded by CSS, and to images or video used with thecrossoriginattribute (for example drawn on a canvas). A plain<img src>from another origin displays without any CORS. - CORS never grants permission. The bucket policy, IAM or a signature still decides whether S3 serves the object; CORS only decides whether the browser lets the script see the answer.
#Simple requests and preflights
Browsers sort cross-origin requests into two kinds:
- Simple requests: GET, HEAD or POST with only “safe” headers and a plain
Content-Type(form data or text). The browser sends the request at once, with anOriginheader, then checks the response forAccess-Control-Allow-Origin. If it is missing or names another origin, the script gets a CORS error even though S3 sent the data. - Preflighted requests: any other method (PUT, DELETE), a custom header (
Authorization,x-amz-*), or aContent-Typesuch asapplication/json. Before sending the real request, the browser sends an OPTIONS request, the preflight, carryingOrigin,Access-Control-Request-MethodandAccess-Control-Request-Headers. Only if the answer allows the origin, the method and every header does the real request go out.
The slides’ picture of the preflight: the browser asks the cross-origin server “may https://www.example.com send GET, PUT, DELETE?”; the answer carries Access-Control-Allow-Origin: https://www.example.com and Access-Control-Allow-Methods: GET, PUT, DELETE; then the real request follows. The Access-Control-Max-Age header lets the browser cache that answer and skip the next preflight (browsers cap it; Chrome at 2 hours).
CORS between a site and a bucket
The page comes from https://www.example.com. Its JavaScript wants data from the bucket assets-example on its S3 REST endpoint, a different origin. The browser enforces the rules; S3 only sends headers that say what it allows.
Scenario
The browser loads the shop’s page. Its origin is scheme + host + port: https://www.example.com (port 443 implied).
#The CORS configuration on a bucket
You add the configuration to the bucket that serves the resource, the cross-origin target, never to the site that makes the request. In the S3 console it is JSON (Permissions → Cross-origin resource sharing); the API and CLI also accept it (put-bucket-cors), and older documents show the same rules in XML. A configuration holds up to 100 rules, each with:
| Element | Meaning |
|---|---|
AllowedOrigins (required) | Origins allowed, such as https://www.example.com. Each string may contain one * wildcard (https://*.example.com), and * alone allows every origin |
AllowedMethods (required) | Any of GET, PUT, POST, DELETE, HEAD |
AllowedHeaders | Headers the browser may ask to send in a preflight (Access-Control-Request-Headers); one * per string, so x-amz-* or * |
ExposeHeaders | Response headers the script is allowed to read, such as ETag (needed for multipart uploads from the browser) or x-amz-meta-* values |
MaxAgeSeconds | How long the browser may cache the preflight answer |
ID | An optional name for the rule |
S3 checks the rules in order and uses the first rule whose origin, method and (for a preflight) every requested header match. If none matches, a preflight gets 403 Forbidden (“This CORS request is not allowed”, or “CORS is not enabled for this bucket” when there is no configuration at all), and the browser never sends the real request.
[
{
"ID": "public-read-anywhere",
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedHeaders": []
},
{
"ID": "admin-uploads",
"AllowedOrigins": ["https://www.example.com", "https://admin.example.com"],
"AllowedMethods": ["PUT", "POST", "DELETE"],
"AllowedHeaders": ["content-type", "x-amz-*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000
}
]
The slides’ S3 example is a page served from one website bucket (my-bucket-html) that loads coffee.jpg from a second website bucket (my-bucket-assets). The second bucket must allow the first bucket’s website endpoint, http://my-bucket-html.s3-website.us-west-2.amazonaws.com, as an origin, or *. Note the http://: a rule for https:// would not match.
Try every combination, including the rule-order trap and the case where CORS passes but the bucket policy refuses:
CORS preflight simulator
A page’s script calls the bucket assets-example on its S3 REST endpoint. Pick the bucket’s CORS configuration and the request, then send it and watch what the browser and S3 do.
CORS configuration on assets-example
[
{
"AllowedOrigins": [
"https://www.example.com"
],
"AllowedMethods": [
"GET",
"HEAD"
],
"AllowedHeaders": []
}
]Method
Extra request headers
Press “Send request” to see what happens.
#CORS mistakes that cost an afternoon
- The rule is on the wrong bucket. It belongs on the bucket the browser is fetching from.
- The origin differs by a scheme, a port or a
www.http://vshttps://,:8443,example.comvswww.example.comare all different origins. A trailing slash in the rule (https://www.example.com/) never matches either. - A header is missing from
AllowedHeaders. A PUT from the AWS SDK sendscontent-typeand severalx-amz-*headers; list them or use*. - The script cannot read
ETag. Add it toExposeHeaders. - A CDN cached the wrong answer. With CloudFront in front, forward the
Origin,Access-Control-Request-MethodandAccess-Control-Request-Headersheaders and includeOriginin the cache key (the managedCORS-S3Originorigin request policy does this), and allow the OPTIONS method; otherwise one origin’s cached response, without CORS headers, is served to another. S3 addsVary: Originfor this reason. - Expecting CORS to protect data. It does not: anyone can call S3 without a browser. Protect data with policies; use CORS only to let your own pages read it.
#Static sites in system design
- The serverless web front end. Static files in S3, served through CloudFront with OAC, HTTPS and a custom domain; dynamic calls from the browser to API Gateway and Lambda, or to an ALB. No web servers to patch, and it scales to any rush. Most of chapter 15 builds on this shape.
- Split pages from heavy assets. Pages on the main domain, large media in a separate bucket or behind a separate path. With different origins, add CORS on the asset bucket; with one CloudFront distribution and path behaviours (
/assets/*), everything is same-origin and CORS is not needed at all. - Direct browser uploads. The browser PUTs files straight into S3 with a pre-signed URL (lesson 10.12), so your servers never handle the bytes. This needs a CORS rule for PUT (and
ETagexposed for multipart). - A maintenance or failover page. A tiny S3 website as the secondary record of a Route 53 failover policy (lesson 8.3) shows “we’ll be right back” when the main site is down.
- Know the limits. HTTP only, public only, no server-side code, Regional (one copy in one Region unless CloudFront caches it). If you need any of these, put CloudFront in front or choose another service.
#Try it yourself
With the AWS CLI, in a folder that holds index.html, 404.html and the rest of the site:
# 1. Create the bucket and upload the site (content types are guessed from the extensions)
aws s3 mb s3://mishti-menu-example --region ap-south-1
aws s3 sync ./site s3://mishti-menu-example/
# 2. Turn on website hosting with an index and an error document
aws s3 website s3://mishti-menu-example/ --index-document index.html --error-document 404.html
# 3. Allow public reads: unblock public access for this bucket, then add the policy
aws s3api put-public-access-block --bucket mishti-menu-example \
--public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=false,RestrictPublicBuckets=false
aws s3api put-bucket-policy --bucket mishti-menu-example --policy file://public-read.json
# 4. Test it (dot-Region endpoint for ap-south-1)
curl -I http://mishti-menu-example.s3-website.ap-south-1.amazonaws.com/
curl -I http://mishti-menu-example.s3-website.ap-south-1.amazonaws.com/menu # 302 → /menu/
curl -I http://mishti-menu-example.s3-website.ap-south-1.amazonaws.com/nothing # 404
curl -I https://mishti-menu-example.s3.ap-south-1.amazonaws.com/ # REST: 403 (list)
# 5. Routing rules and an object redirect
aws s3api put-bucket-website --bucket mishti-menu-example --website-configuration file://website.json
aws s3api copy-object --bucket mishti-menu-example --copy-source mishti-menu-example/old-menu.html \
--key old-menu.html --website-redirect-location /menu/ --metadata-directive REPLACE
aws s3api get-bucket-website --bucket mishti-menu-example
Step 3 keeps the two ACL settings on (ACLs are not used) and turns off only the two that block public policies. website.json holds the index, error and routing rules together:
{
"IndexDocument": { "Suffix": "index.html" },
"ErrorDocument": { "Key": "404.html" },
"RoutingRules": [
{ "Condition": { "KeyPrefixEquals": "docs/" }, "Redirect": { "ReplaceKeyPrefixWith": "documents/" } }
]
}
CORS on the assets bucket, then a preflight by hand (curl plays the browser and sends the headers a browser would):
aws s3api put-bucket-cors --bucket assets-example --cors-configuration file://cors.json
aws s3api get-bucket-cors --bucket assets-example
curl -i -X OPTIONS https://assets-example.s3.ap-south-1.amazonaws.com/photos/roshogolla.jpg \
-H "Origin: https://admin.example.com" \
-H "Access-Control-Request-Method: PUT" \
-H "Access-Control-Request-Headers: content-type"
# 200 with Access-Control-Allow-Origin if a rule matches; 403 if none does
aws s3api delete-bucket-cors --bucket assets-example
For the API the file needs a wrapper: {"CORSRules": [ …the rules… ]}; the console takes the bare list.
#Summary
Key takeaways
- S3 hosts static sites (HTML, CSS, JavaScript, images, SPAs built with React or Angular) with no server; it cannot run server-side code (PHP, Node.js, Python). Hosting itself is free: you pay storage, requests and transfer out.
- Setup: enable hosting → index document (required) and error document (optional, 4xx only) → Block Public Access off (bucket and account) → bucket policy allowing public
s3:GetObjectonarn:aws:s3:::bucket/*. - Endpoint:
http://bucket.s3-website-region.amazonaws.com(older Regions) orhttp://bucket.s3-website.region.amazonaws.com(the rest). HTTP only. - 403 Forbidden → not publicly readable: Block Public Access, no or wrong policy, objects owned by another account, SSE-KMS, Requester Pays, an explicit Deny. 404 → no such key or missing index document. 302 → folder without a trailing slash.
- Website endpoint vs REST endpoint: public only vs public and private, GET/HEAD only vs all operations, index documents, HTML vs XML errors, redirects, no HTTPS vs HTTPS.
- Redirects: a whole bucket to another host (www → apex), routing rules (prefix or error code, up to 50), or per-object
x-amz-website-redirect-location. - Custom domain: bucket named exactly like the domain + Route 53 Alias. HTTPS: CloudFront (best with a private bucket and OAC), or Amplify Hosting.
- Origin = scheme + host + port. The browser enforces the same-origin policy; CORS headers from the other origin relax it. Simple requests carry
Origin; others are preflighted with OPTIONS. - S3 CORS configuration goes on the bucket serving the resource: JSON rules with
AllowedOrigins(one*, or*alone),AllowedMethods(GET, PUT, POST, DELETE, HEAD),AllowedHeaders,ExposeHeaders,MaxAgeSeconds; up to 100 rules, first match wins. CORS never grants access.
Answer on your own first, then read the explanation, even when you got it right.
Self-checkQuestion
Which of these can S3 static website hosting serve on its own?
Self-checkQuestion
Which value must the static website hosting configuration always contain when you host a site (rather than redirect all requests)?
Self-checkQuestion
Which two URLs are valid S3 website endpoints?
Choose 2
Self-checkQuestion
A team enables static website hosting on a new bucket, uploads index.html and opens the website endpoint. The browser shows 403 Forbidden. What is the most likely cause?
Self-checkQuestion
Block Public Access is off on the bucket, and the bucket policy allows Principal * the action s3:GetObject on arn:aws:s3:::shop-example. Visitors still get 403. Why?
Self-checkQuestion
The bucket-level Block Public Access settings are off and the public-read policy is correct, but the console refuses to save the policy with an access denied message about public policies. What should you check?
Self-checkQuestion
The website works for every page except the downloadable menu files (.pdf), which return 403. They were uploaded by a script with a different encryption setting. What is the most likely cause?
Self-checkQuestion
On a public website bucket, a visitor requests /menu (no trailing slash). There is no object called menu, but menu/index.html exists. What does S3 return?
Self-checkQuestion
An S3 website has 404.html configured as its error document. For which response does S3 return that document?
Self-checkQuestion
A React single-page app is hosted on an S3 website endpoint. Users who refresh the page at /orders/42 get the error page instead of the app. What is the simplest fix that keeps the S3 website endpoint?
Self-checkQuestion
Which two statements about the S3 website endpoint, compared with the REST endpoint, are true?
Choose 2
Self-checkQuestion
A site’s JavaScript uploads files with PUT to http://shop-example.s3-website-us-east-1.amazonaws.com and gets 405. What should it do instead?
Self-checkQuestion
Object page1.html has the metadata x-amz-website-redirect-location set to /page2.html. What happens when a visitor requests page1.html through the website endpoint, and through the REST endpoint?
Self-checkQuestion
A company wants both http://example.com and http://www.example.com to show the same S3-hosted site, with only one copy of the files. What is the standard setup?
Self-checkQuestion
A website has more than 200 individual pages that were renamed. Which redirect method fits best?
Self-checkQuestion
Route 53 hosts example.com. The site is in the bucket company-site-prod with website hosting on. When creating an Alias record for example.com, the bucket does not appear as a target. Which two fixes work?
Choose 2
Self-checkQuestion
The marketing site on an S3 website endpoint must be served over HTTPS at www.example.com, as cheaply and simply as possible on AWS. What should the architect do?
Self-checkQuestion
A security review says the S3 bucket holding the public website must not be publicly accessible, yet the site must stay public on HTTPS. Which design meets this?
Self-checkQuestion
Which pair of URLs shares the same origin?
Self-checkQuestion
Pages are served from https://www.example.com. The page’s fonts and a JSON price list are fetched from https://assets-example.s3.us-east-1.amazonaws.com and the browser console shows CORS errors. Where must CORS be configured?
Self-checkQuestion
With curl, the developer downloads prices.json from the bucket without problems, but the same request from the web page fails with a CORS error. Why?
Self-checkQuestion
Which request from a page at https://www.example.com to an S3 bucket triggers a CORS preflight?
Self-checkQuestion
Which two elements can appear in an S3 CORS rule?
Choose 2
Self-checkQuestion
A bucket has this CORS configuration: rule 1 allows GET from https://www.example.com with no ExposeHeaders; rule 2 allows GET and PUT from https://www.example.com and exposes x-amz-meta-shop. A script on www.example.com sends a simple GET and tries to read the x-amz-meta-shop header. What happens?
Self-checkQuestion
A CORS rule allows AllowedOrigins [https://www.example.com] and AllowedMethods [GET, PUT], with no AllowedHeaders. The SDK in the browser uploads with PUT and sends content-type and x-amz-checksum-crc32 headers. The preflight fails with 403. What is the fix?
Self-checkQuestion
A site served from the website endpoint http://shop-html.s3-website-us-west-2.amazonaws.com loads images with fetch() from the bucket shop-assets. Its CORS rule allows the origin https://shop-html.s3-website-us-west-2.amazonaws.com. The images are blocked. Why?
Self-checkQuestion
CloudFront sits in front of an S3 bucket with a correct CORS configuration allowing two origins. Users on the second origin sometimes get CORS errors, and the errors go away after cache invalidation. What is the cause and fix?
Self-checkQuestion
Which two statements about CORS on S3 are true?
Choose 2
Self-checkQuestion
A static site on an S3 website endpoint is visited 2,000,000 times a month; each visit downloads 1.5 MB. Ignoring the free tier and assuming $0.09 per GB of data transfer out, roughly what does transfer out cost per month?
Self-checkQuestion
Which two statements in the original notes needed correcting?
Choose 2