Host a Website on Amazon S3
Put your first website on the internet using nothing but a storage bucket.
Related projects
The 30-second version
stores files of any size and hands them back to anyone you allow, from anywhere.
A simple website is only a handful of files. Put those files in an S3 bucket, switch on , give the public permission to read them, and S3 serves them as a website. No server to build or patch.
Why start here?
Static hosting on S3 is the smallest real cloud deployment you can make. You will meet three ideas that come up in almost every AWS project after this one: buckets and objects, public access controls, and policies written in JSON.
Start your project here
Pick how much help you want. You can switch at any time and your progress stays put.
How this page works
Each step ends with an evidence task: a screenshot plus a sentence or two in your own words. Tick Mark step as complete as you go. When you finish, open My record to print or save a PDF of your work for your portfolio. Nothing is uploaded anywhere; it all stays in this browser.
Before we start
In this step, get ready to:
- Check you have what you need.
- Choose the AWS Region you will work in.
- Know what this project will cost (very little).
What you need
- An AWS account. If you don't have one yet, follow the AWS guide linked under Related projects.
- A sign-in that isn't the root user. An IAM Identity Center user or an IAM user with S3 permissions is the safer habit.
- A computer with a modern browser and a way to unzip files.
What it costs
A few small files and a handful of page views cost a fraction of a US cent. Accounts opened after 15 July 2025 also start on the AWS Free plan with sign-up credits. The only way this gets expensive is forgetting the bucket for months, which is why the last step deletes everything.
Pick a Region
A Region is a cluster of AWS data centres in one part of the world. Your bucket lives in exactly one. Any Region works for this project; one close to your visitors loads a little faster.
Learning from West Africa?
Africa (Cape Town), af-south-1, is an opt-in Region, so it has to be enabled in your account settings before it appears. If you'd rather not, Europe (London) eu-west-2 or Europe (Ireland) eu-west-1 are fine choices.
Evidence: your starting point
To doIn a sentence or two: which Region did you choose, and why?
Create an S3 bucket
Every file in S3 lives inside a bucket. Yours will hold the whole website.
In this step, get ready to:
- Open Amazon S3 in the AWS console.
- Create a general purpose bucket with a name nobody else in the world is using.
- Allow public access for this one bucket.
We use it to build your bucket name and to put on your completion record. It never leaves this browser.
Your bucket name:
itsolowils-s3-site-yourname-abcd
The four characters at the end are random and saved for you, so two learners with the same name won't clash.
Key settings
- Bucket type: General purpose
- Object Ownership: ACLs disabled (recommended)
- Block all public access: cleared, with the warning acknowledged
- Bucket Versioning: Disable · Default encryption: leave as it is
- Sign in to the AWS Management Console.
- In the search bar at the top, type S3 and choose S3.
- Check the Region selector in the top-right corner and switch it to the Region you picked in Step 0.
- Choose Create bucket.
- If you're asked for a Bucket type, keep General purpose.
- Paste your bucket name from the box above into Bucket name.
- Under Object Ownership, leave ACLs disabled (recommended) selected.
- Under Block Public Access settings for this bucket, clear the Block all public access checkbox.
- A warning appears. Tick the box that acknowledges the bucket and its objects might become public. For a website, that is the point.
- Leave Bucket Versioning on Disable and the encryption settings as they are.
- Scroll down and choose Create bucket. Your bucket appears in the list.
Public buckets are for public files only
Turning off Block Public Access on this bucket is fine because everything in it is meant for the whole internet. Never do this on a bucket holding backups, reports or anything personal.
Why does the name have to be unique?
Bucket names share one namespace across every AWS account, because the name becomes part of a web address. Once you own itsolowils-s3-site-…, nobody else can create a bucket with that name until you delete yours. Names must be 3 to 63 characters of lowercase letters, numbers, hyphens and dots.
What is Block Public Access protecting me from?
Block Public Access is a safety switch that overrides any rule granting public access, even if someone writes one by mistake. It is on by default for every new bucket. Clearing it doesn't make anything public by itself. It only allows a public rule to work. You'll write that rule in Step 4.
Why leave ACLs disabled?
Access control lists are an older way of granting permissions file by file. AWS now recommends leaving them off and controlling access with one bucket policy instead. It's easier to read, easier to audit and covers every object at once.
🛠 Troubleshooting: the console won't accept my bucket name
- "Bucket with the same name already exists": someone, somewhere, owns it. Change the last four characters and try again.
- "Bucket name must not contain uppercase characters": lowercase only, no spaces or underscores.
Evidence: your new bucket
To doScreenshot the bucket list showing your bucket and its Region. Then explain, in your own words, what Block Public Access does.
Upload your website files
An empty bucket isn't much of a website. Time to fill it.
In this step, get ready to:
- Download the starter website and make it yours.
- Upload its files into your bucket as objects.
Download starter-site.zip (2 KB)
Inside you'll find index.html, error.html and an assets folder with a stylesheet and an icon.
Key settings
- Upload
index.htmlanderror.htmlwith Add files. - Upload the
assetsfolder with Add folder, so the keys start withassets/. - All three should sit at the top level of the bucket, not inside
starter-site/.
- Unzip
starter-site.zip. You get a folder calledstarter-site. - Optional but worth it: open
index.htmlin a text editor (Notepad, TextEdit, VS Code), change Your Name in the heading to your name, and save. - Back in the S3 console, choose your bucket's name to open it.
- On the Objects tab, choose Upload.
- Choose Add files and select
index.htmlanderror.htmlfrom inside thestarter-sitefolder. - Choose Add folder and select the
assetsfolder. Confirm if your browser asks whether to upload its files. - Check the list: you should see
index.html,error.html,assets/style.cssandassets/cloud.svg. - Choose Upload, wait for the green success banner, then choose Close.
Hot tip for Windows users
Windows sometimes unzips into a folder inside a folder with the same name. If you open starter-site and find another starter-site, go one level deeper before you pick your files.
Wait, S3 has folders?
Not really. S3 is flat: every object has a key, which is just its full name, like assets/style.css. The console draws a folder whenever a key contains a slash, so it looks familiar. This matters in Step 4: if you upload the whole starter-site folder, your homepage's key becomes starter-site/index.html and S3 won't find it at the top.
What do HTML and CSS actually do?
HTML describes what's on a page: headings, paragraphs, images, links. CSS describes how it looks: colours, fonts, spacing. A browser downloads both and puts the page together. Because nothing has to run on the server, the site is called static.
Evidence: your objects
To doScreenshot the Objects tab showing index.html, error.html and the assets/ folder. Note what you changed in index.html, if anything.
Turn on static website hosting
Bucket: done. Files: done. Now tell S3 to treat the bucket as a website.
In this step, get ready to:
- Switch on static website hosting with an index and an error document.
- Find your bucket's website endpoint and open it.
- Meet your first error, on purpose.
Key settings
- Properties tab → Static website hosting → Edit
- Enable · Host a static website
- Index document
index.html· Error documenterror.html
- Inside your bucket, choose the Properties tab.
- Scroll all the way down to the Static website hosting panel and choose Edit.
- Set:
- Static website hosting: Enable
- Hosting type: Host a static website
- Index document:
index.html - Error document:
error.html
- Choose Save changes.
- Scroll back down to Static website hosting. Under Bucket website endpoint, open the link in a new tab.
Your endpoint looks like one of these, depending on the Region:
http://bucket-name.s3-website-region.amazonaws.com
http://bucket-name.s3-website.region.amazonaws.com
403 Forbidden? Good.
You should see 403 Forbidden with the code AccessDenied. Keep that tab open. It proves the website is switched on but nobody has been given permission to read your files yet.
Why do I get 403 Forbidden?
Everything in S3 is private until a rule says otherwise. In Step 1 you removed the safety switch that blocks public rules, but you haven't written a rule that allows anything. Think of a shop with the shutters up and the door still locked. Step 4 unlocks the door.
What's special about the website endpoint?
S3 has two ways in. The normal endpoint lists and fetches objects for programs. The website endpoint behaves like a web server: it returns index.html when someone visits the root, serves error.html when a page is missing, and supports redirects. It only speaks plain HTTP, which is why your browser may label it "Not secure". Adding HTTPS with CloudFront is the next project.
Evidence: the 403 error
To doScreenshot the 403 Forbidden page with the address bar visible. Explain why it happens.
Go public with a bucket policy
One small JSON document stands between you and a live website.
In this step, get ready to:
- Write a bucket policy that lets anyone read your files.
- Refresh the 403 tab and see your website live.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForWebsite",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::itsolowils-s3-site-yourname-abcd/*"
}
]
}
Your bucket name is already filled in. If you changed it in the console, edit the Resource line to match.
Write a policy that allows everyone to perform s3:GetObject on every object in your bucket, then attach it under Permissions → Bucket policy.
Reveal the policy if you need it
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForWebsite",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::itsolowils-s3-site-yourname-abcd/*"
}
]
}
Want to find the policy editor yourself first?
Good call. Hint: permissions live on a tab with a very obvious name. Come back and open the clicks if you get lost.
- Switch to the S3 console tab and open your bucket.
- Choose the Permissions tab.
- In the Bucket policy panel, choose Edit.
- Copy the policy above and paste it into the editor, replacing anything already there.
- Choose Save changes. The bucket now shows a Publicly accessible badge.
- Go back to the tab showing 403 Forbidden and refresh.
It's alive
Refresh the endpoint tab. Your page appears, styled and with your name on it. Share the link with a friend and it will work for them too.
Read the policy line by line
"Version": "2012-10-17"is the policy language version. It's a fixed value, not today's date."Effect": "Allow"means this statement grants permission."Principal": "*"means anyone, including people who aren't signed in to AWS."Action": "s3:GetObject"allows reading (downloading) objects and nothing else. Visitors can't upload, list or delete."Resource": "arn:aws:s3:::bucket/*"is the ARN for every object in the bucket. The/*matters: without it the rule points at the bucket itself, not the files inside.
Bucket policies vs ACLs
A bucket policy is one document that governs the whole bucket, so you can read every permission in one place. ACLs attach a permission to each object, which is easy to lose track of. That's why new buckets come with ACLs disabled and AWS points you to policies instead.
🛠 Troubleshooting: it still doesn't work
- "Policy has invalid resource" when saving: the bucket name in
Resourcedoesn't match your bucket, or the/*is missing. - "Access denied" when saving: Block Public Access is still on, either for this bucket (Permissions tab) or for your whole account (S3 menu → Block Public Access settings for this account).
- 404 Not Found, Code: NoSuchKey: S3 can't find
index.htmlat the top of the bucket. Check the spelling in Step 3 and that you didn't upload the wholestarter-sitefolder. Fix it by uploadingindex.htmlagain at the top level. - The page loads but looks plain: the
assetsfolder didn't upload, so the stylesheet is missing. - Still 403 after saving: hard refresh with Ctrl + F5 (Windows) or Cmd + Shift + R (Mac), and make sure you're on the website endpoint from Step 3.
- Browser says "Not secure": expected. Website endpoints are HTTP only.
Evidence: your live website
To doScreenshot your live site with the endpoint visible in the address bar. In one sentence, what does "Principal": "*" mean?
Protect your homepage from deletion
Optional, and the best part. You'll make it impossible to delete index.html, even for yourself, then prove it.
In this mission, get ready to:
- Add a Deny statement to your bucket policy.
- Try to delete
index.htmland watch it fail. - Explain why an explicit deny always wins.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForWebsite",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::itsolowils-s3-site-yourname-abcd/*"
},
{
"Sid": "ProtectHomepage",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:DeleteObject",
"Resource": "arn:aws:s3:::itsolowils-s3-site-yourname-abcd/index.html"
}
]
}
Add a second statement to your policy that denies s3:DeleteObject to everyone on index.html only. Then try deleting it.
- Open Permissions → Bucket policy → Edit.
- Replace the policy with the two-statement version above and choose Save changes.
- Go to the Objects tab, tick
index.htmland choose Delete. - Type permanently delete to confirm and choose Delete objects.
- The console reports that the delete failed with an access denied error. Mission accomplished.
Why does Deny beat my admin permissions?
AWS checks every request against all the policies that apply. An explicit Deny anywhere overrides every Allow, no matter how powerful. That's what makes Deny statements useful as guardrails: they protect critical files from mistakes, including your own. Your account can still edit or remove the bucket policy itself, which is how you'll undo this in the next step.
Evidence: the failed delete
To doScreenshot the failed delete. Where else at work could a Deny guardrail like this be useful?
Delete your resources
Cleaning up is part of the job on every project. It keeps your bill at zero and takes a public bucket off the internet.
In this step, get ready to:
- Remove the bucket policy.
- Empty the bucket.
- Delete the bucket.
Think you can do this without instructions?
Great. Double-check the General purpose buckets list: your bucket should be gone.
- Open your bucket → Permissions → Bucket policy → Delete. Type delete to confirm. This removes the Deny rule from the secret mission.
- Go back to General purpose buckets, select your bucket and choose Empty.
- Type permanently delete and choose Empty, then Exit.
- With the bucket still selected, choose Delete.
- Type your bucket name to confirm and choose Delete bucket.
🛠 Troubleshooting: index.html won't delete
That's the secret mission doing its job. Delete the bucket policy first (step 1 above), then empty the bucket again.
Evidence: clean account
To doScreenshot your bucket list without the project bucket. Why did you have to empty the bucket before deleting it?
You just shipped a website on AWS
Well done.
That wasn't nothing. You took a folder of files and made it a public website using cloud storage alone. Along the way you:
- Created an S3 bucket with a globally unique name.
- Uploaded HTML, CSS and image files as objects.
- Switched on static website hosting with index and error documents.
- Wrote a bucket policy to grant public read access, and a Deny rule to protect a file.
- Cleaned up every resource you created.
Does the dock say you still have tasks left?
Open My record. Anything without a note or screenshot, or flagged Return to later, is listed there with a link straight to it.
Join the live feed
Finished? Put your first name and country on the homepage feed. Completely optional.