Host a Website on Amazon S3
Host a static website on S3 and secure it with IAM least-privilege policies.
Introduction
30 Second Summary
Every website you visit lives on a server somewhere. Companies use cloud storage services to host their websites because it's reliable, scalable, and surprisingly simple to set up.
In this project, you will host your own static website on Amazon S3 and secure it with IAM policies that control exactly who can do what with your files.
What You'll Build
A publicly accessible static website hosted on an S3 bucket, secured with a custom IAM policy that enforces least-privilege access.
By the end of this project, you'll have:
- A live website hosted on Amazon S3 that anyone can visit via a public URL.
- A bucket policy that grants public read access to your website files.
- An IAM user with a custom least-privilege policy scoped to uploading files to your specific bucket.
- Secret Mission: Add a custom 404 error page and style your website with CSS to make it portfolio-ready.
Are there any prerequisites?
No prerequisites needed. This guide includes full setup instructions, starting from creating your AWS account. All you need is a web browser and a credit or debit card for account verification.
Create Your AWS Account
To host a website on Amazon S3, you need an AWS account.
In this step, you will create a free AWS account, sign in to the console, and pick the region where your website will live.
In this step, get ready to:
- Sign up for an AWS Free Tier account.
- Sign in to the AWS Management Console.
- Set your AWS Region.
Sign up for an AWS account
AWS offers a Free Tier plan that gives new customers $100 in credits on sign-up. You will not be charged for anything in this project.
- Open https://aws.amazon.com/free/ in your browser.
- Click Create a free account.
- Enter your email address and an account name, then click Verify email address.
- Check your email for a verification code and enter it on the next screen.
- Create a strong root user password and click Continue.
- Choose your account plan. Select Free.
- Choose Personal as your account type and fill in your contact information.
- Read and accept the AWS Customer Agreement, then click Continue.
- Enter your credit or debit card details on the billing information page.
- Verify your phone number by entering the PIN sent to you via SMS or voice call.
- Select the Basic Support plan (free of charge).
- Click Complete sign up.
Why do I need a credit card?
AWS requires a valid payment method to verify your identity and prevent abuse of resources. AWS will not charge your payment method unless you upgrade to the Paid plan.
On the Free plan, your account closes automatically after 6 months or when your credits run out. You will not receive surprise bills.
What if activation takes a while?
Account activation usually takes a few minutes but can sometimes take up to 24 hours. Check your email (including spam) for a confirmation message from AWS.
Sign in to the AWS Management Console
Once your account is activated, you can access the AWS Management Console. This is the web dashboard where you manage all your AWS services.
- Open https://console.aws.amazon.com/ in your browser.
- Enter the email address and password you used during sign-up.
- Click Sign in.
You should now see the Console Home dashboard.
Can't sign in?
Make sure you are signing in as the Root user (not an IAM user). The sign-in page has a toggle between root user and IAM user. Select root user and enter the email you signed up with.
If you have not received an activation email yet, wait a few more minutes and check your spam folder. Help me sign in to the AWS Console
Set your AWS Region
Every AWS resource lives in a specific geographic region. Choosing a region close to you reduces loading time for your website.
- In the top-right corner of the console, find the region dropdown (it shows a region name like US East (N. Virginia)).
- Click the dropdown and select a region geographically close to you.
- If you are unsure, select US East (N. Virginia) us-east-1.
What is an AWS Region?
An AWS Region is a physical location around the world where AWS runs its data centers. Your S3 bucket will be created in whichever region you select here.
Your AWS account is ready to go. Next up, you will create an S3 bucket and turn it into a live website.
Create an S3 Bucket and Enable Website Hosting
Your AWS account is ready. Now it's time to create the storage container that will hold your website files.
Amazon S3 stores files in containers called buckets. In this step, you'll create a bucket, turn on its built-in website hosting feature, and upload your first web page.
In this step, get ready to:
- Create an S3 bucket with a globally unique name.
- Enable static website hosting on the bucket.
- Upload an index.html file to the bucket.
Navigate to S3 and create a bucket
Every file you upload to S3 lives inside a bucket. A bucket is like a top-level folder with a name that must be unique across all of AWS.
- In the console search bar at the top, type S3 and select S3 from the results.
- In the left navigation pane, click General purpose buckets.
- Click Create bucket.
- In the Bucket name field, enter a globally unique name (for example, my-first-website-yourname). Replace yourname with your actual name to make it unique.
- Leave all other settings as their defaults.
- Scroll to the bottom and click Create bucket.
Why does my bucket name need to be globally unique?
Your bucket name becomes part of the public URL for your website. Since every S3 bucket across all AWS accounts shares one global namespace, no two buckets anywhere in the world can have the same name.
Use lowercase letters, numbers, and hyphens only. Adding your name or initials (like my-first-website-janedoe) is a simple way to keep it unique.
Seeing a "Bucket name already exists" error?
Someone else already owns that name. Try adding more characters to make yours unique (for example, add numbers or your full name).
Help me pick a unique S3 bucket name.
Enable static website hosting
S3 buckets can serve files directly as a website. You need to turn on this feature and tell S3 which file to show as the home page.
- Click your new bucket name to open it.
- Click the Properties tab.
- Scroll down to the Static website hosting section and click Edit.
- Select Enable.
- In the Index document field, enter index.html.
- In the Error document field, enter 404.html.
- Click Save changes.
You are now back on the Properties tab. Scroll down to the Static website hosting section again. You should see a Bucket website endpoint URL displayed.
- Copy this URL and save it somewhere (for example, in a new browser tab or a text file). This is your website's public address.
What is the bucket website endpoint?
This is the public URL where anyone can view your website once it's live. It looks something like http://your-bucket-name.s3-website-us-east-1.amazonaws.com. The exact format depends on your region.
S3 website endpoints only support HTTP. For HTTPS, you would add CloudFront in front, but that is outside the scope of this project.
Why did I set an error document?
The error document (404.html) tells S3 which page to show when someone visits a URL that does not exist on your site. You will create this file in the Secret Mission later.
Create and upload your index.html file
Your bucket needs an actual web page to serve. You will create a simple HTML file on your computer and upload it to S3.
- Press the Windows key (Windows) or Cmd+Space (macOS) to open your search bar.
- Type Notepad (Windows) or TextEdit (macOS) and press Enter to open it.
- Copy and paste the following HTML into the editor:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My Website Home Page</title>
</head>
<body>
<h1>Welcome to my website</h1>
<p>Now hosted on Amazon S3!</p>
</body>
</html>
What does this HTML do?
This is a minimal web page. The <title> sets the text that appears in the browser tab. The <h1> creates a heading and the <p> creates a paragraph of text on the page.
- Save the file as index.html on your Desktop. On Windows in Notepad, click File then Save As. Change Save as type to All Files (*.*), type index.html as the file name, and click Save.
Make sure the file extension is .html
If you skip changing the file type to All Files (*.*), Notepad may save it as index.html.txt instead. S3 needs the file name to match index.html exactly for website hosting to work.
- Switch back to the S3 console in your browser.
- Click the Objects tab in your bucket.
- Click Upload.
- Click Add files and select the index.html file from your Desktop.
- Click Upload.
You should see a green success banner at the top of the page confirming your upload succeeded.
- Click Close to return to your bucket.
You should now see index.html listed under the Objects tab.
Don't see index.html in your bucket?
Make sure you clicked the final Upload button on the upload page (not just Add files). If your file is named index.html.txt, delete it and re-save from Notepad with the file type set to All Files (*.*).
Help me upload my index.html file to S3.
Your bucket is created, website hosting is enabled, and your home page is uploaded. Next up, you'll make your website publicly accessible by configuring a bucket policy.
Make Your Website Public with a Bucket Policy
Your index.html file is uploaded and your bucket is configured for website hosting. However, if you try visiting your website URL right now, you will get an access denied error.
By default, Amazon S3 blocks all public access to your bucket. To make your website visible to the world, you need to disable this protection and add a bucket policy that explicitly grants read access to everyone.
In this step, get ready to:
- Disable Block Public Access settings on your bucket.
- Add a bucket policy that grants public read access.
- Test your live website in the browser.
Disable Block Public Access
Block Public Access is a safety feature that prevents accidental exposure of your bucket's contents. For a public website, you need to turn it off so visitors can view your pages.
- In the S3 console, click your bucket name to open it.
- Click the Permissions tab.
- Under Block public access (bucket settings), click Edit.
- Uncheck Block all public access.
- Click Save changes.
- A confirmation dialog appears. Type confirm in the text field and click Confirm.
Why does AWS ask me to confirm?
Disabling Block Public Access makes your bucket eligible to be accessed by anyone on the internet. AWS adds this confirmation step to prevent accidental exposure of sensitive data.
For a static website this is exactly what you want. Your visitors need to be able to read your HTML files.
Don't see the Block public access section?
Make sure you are on the Permissions tab (not Properties or Objects). The Block public access settings are near the top of the Permissions page.
Help me find the Block Public Access settings in my S3 bucket.
Add a bucket policy for public read access
With Block Public Access disabled, you can now add a policy that tells S3 to allow anyone to read objects in your bucket. A bucket policy is a JSON document that defines who can do what with your bucket's contents.
- Still on the Permissions tab, scroll down to Bucket policy and click Edit.
- Paste the following policy into the editor, replacing the bucket name with your actual bucket name:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::[[BUCKET_NAME="my-first-website-yourname"]]/*"
]
}
]
}
What does this policy do?
This policy has one statement that grants permission. Here is what each part means:
- "Principal": "*" means everyone (any visitor on the internet).
- "Action": ["s3:GetObject"] means they can only read (download) objects. They cannot upload, delete, or modify anything.
- "Resource" specifies which bucket and objects the permission applies to. The /* at the end means all objects inside your bucket.
- Click Save changes.
You should see a red Publicly accessible warning banner appear on your bucket. This confirms the policy is active.
Seeing an Access Denied error when saving?
If you get an error when saving the bucket policy, Block Public Access may still be enabled. Go back to the top of the Permissions tab and confirm that Block all public access is unchecked.
Also double-check that the bucket name in your policy's Resource field matches your actual bucket name exactly.
Help me fix the Access Denied error when saving my bucket policy.
✔️ Awesome, I've got everything!
Great. Make sure you have saved the bucket policy before moving on.
ⓧ I'd like to double check the full code
Here is the complete bucket policy. Make sure your bucket name replaces your-bucket-name in the Resource field.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::your-bucket-name/*"
]
}
]
}
Test your live website
Your bucket policy is in place. Time to see your website live on the internet.
- Click the Properties tab on your bucket.
- Scroll down to Static website hosting.
- Click the Bucket website endpoint URL. It opens in a new tab.
You should see your website displayed in the browser with the heading "Welcome to my website" and the paragraph "Now hosted on Amazon S3!" below it.
Seeing a 403 Forbidden error?
Make sure you completed both previous substeps. The bucket policy will not work unless Block Public Access is fully disabled first.
Also verify that the file name in your bucket is exactly index.html (lowercase). The index document name is case-sensitive and must match exactly.
Help me troubleshoot my 403 Forbidden error on S3 static website.
Your website is live on the internet. Anyone with the URL can visit it. Next up, you will create an IAM user with restricted permissions to demonstrate how AWS access control works in practice.
Create an IAM User with Least-Privilege Access
Your website is live and anyone on the internet can visit it. But right now, your root account has full control over everything in AWS.
In a real team, you would never give everyone the root password. Instead, you create separate IAM users with only the permissions they need. This is called least-privilege access, and it is a foundational security practice in AWS.
In this step, get ready to:
- Create an IAM user with console access.
- Write a custom IAM policy that only allows uploading files to your bucket.
- Test the IAM user to confirm the policy works.
Create an IAM user with console access
An IAM user is a separate identity inside your AWS account. You will create one called website-uploader that can sign in to the console with its own password.
- Type IAM in the console search bar at the top of the page and select IAM from the results.
- In the left sidebar, click Users.
- Click Create user.
- Enter website-uploader as the username.
- Check the box next to Provide user access to the AWS Management Console.
- Select I want to create an IAM user.
- Under Console password, choose Custom password and type a password you will remember.
- Uncheck Users must create a new password at next sign-in.
- Click Next.
Why uncheck the password reset?
In a real company, you would require users to change their password on first sign-in. For this learning exercise, unchecking it simplifies the testing process so you can sign in immediately without an extra step.
Create a custom policy and attach it to the user
Instead of giving this user full S3 access, you will write a custom policy that allows only one action (s3:PutObject) on only one resource (your specific bucket).
- On the Set permissions page, select Attach policies directly.
- Click Create policy. This opens a new browser tab.
Keep the user creation tab open
The policy editor opens in a new tab. You will return to the user creation tab after creating the policy.
- In the policy editor, click the JSON tab to switch to the JSON editor.
- Delete any existing content in the editor and paste the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowUploadOnly",
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::[[BUCKET_NAME="my-first-website-yourname"]]/*"
]
}
]
}
What does this policy do?
This policy grants exactly one permission: s3:PutObject. That means the user can upload files, but cannot read, delete, or list anything.
The Resource field uses an ARN (Amazon Resource Name) to scope the permission to your specific bucket. The /* at the end means "all objects inside this bucket."
- Click Next.
- In the Policy name field, enter S3WebsiteUploaderPolicy.
- Click Create policy.
You should see a success banner confirming the policy was created.
- Switch back to the browser tab where you were creating the user (the Set permissions page).
- Click the refresh button (circular arrow icon) next to the policy list to reload it.
- In the search box, type S3WebsiteUploaderPolicy.
- Check the box next to S3WebsiteUploaderPolicy in the results.
- Click Next.
- Review the summary, then click Create user.
You should see a success page confirming the user was created. Note the Console sign-in URL displayed on this page. You will use it in the next substep.
Don't see your policy in the list?
Make sure you clicked the refresh button after switching back to the user creation tab. The policy list does not auto-update.
If you still cannot find it, check that you named it exactly S3WebsiteUploaderPolicy with no extra spaces.
Help me find my custom policy in IAM.
What is least-privilege access?
Least-privilege means giving a user only the minimum permissions required to do their job. Your website-uploader user can upload files to one bucket and nothing else.
In a real DevOps environment, every team member, application, and automated process should follow this principle. It limits the damage if credentials are ever compromised.
Test the IAM user in a private browser window
The best way to prove the policy works is to sign in as the new user and try actions that should be allowed and actions that should be blocked.
- Open a private or incognito browser window.
- Paste the Console sign-in URL you noted from the user creation success page.
- Sign in with the username website-uploader and the custom password you set earlier.
- Once signed in, type S3 in the console search bar and select S3.
What should I see?
You may see an access denied error or an empty bucket list. This is expected because your policy does not include s3:ListAllMyBuckets or s3:ListBucket permissions. The user can only upload, not browse.
- In the S3 search bar, type your bucket name (e.g., my-first-website-yourname) and navigate to it.
- Click Upload.
- Drag in any small file from your computer (a text file or image).
- Click Upload.
The upload should succeed. This confirms the s3:PutObject permission is working.
- Now try to delete the file you just uploaded by selecting it and clicking Delete.
The delete should fail with an access denied error. This proves the policy is enforcing least-privilege correctly.
Upload failed too?
Double-check that the bucket name in your policy's Resource field matches your actual bucket name exactly, including capitalization.
Also verify that you attached S3WebsiteUploaderPolicy to the website-uploader user (check the user's Permissions tab).
My IAM user cannot upload to S3. Help me debug.
Secret mission
Make It Portfolio-Ready
Your website works, but it looks like plain HTML from 1995. In this secret mission, you will add custom CSS styling and a friendly 404 error page to make your site something you would actually show an employer.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. Your S3 bucket and IAM user are covered by your AWS Free Tier credits for 6 months, so there are no ongoing charges for this project during that period.
Cost warning
After your 6-month AWS Free Tier period expires, S3 storage and request costs will apply. For a small static website the charges are minimal (fractions of a cent per month), but if you do not plan to keep the website running, delete the resources to avoid any future charges.
Resources you used:
- 1 S3 bucket with static website hosting enabled (containing index.html, style.css, and 404.html).
- 1 IAM user (website-uploader) with console access.
- 1 IAM policy (S3WebsiteUploaderPolicy).
Keep everything running
No action needed. Choose this if you are still actively building or want your website to stay live at its S3 endpoint.
- Your website remains publicly accessible at the bucket website endpoint URL.
- Storage costs are covered by your Free Tier credits for 6 months.
- The IAM user website-uploader remains active with upload-only permissions.
Pause - I'll come back to this later
Block public access to take your website offline while keeping all files and settings intact. You can re-enable public access later to bring the site back.
- In the AWS console, navigate to your S3 bucket.
- Click the Permissions tab.
- Under Block public access (bucket settings), click Edit.
- Check Block all public access and click Save changes.
- Type confirm when prompted.
Your website will no longer be accessible publicly, but all files and configuration remain in place. To bring your site back online, reverse this step by unchecking Block all public access.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow each step below in order.
Delete all objects in your S3 bucket:
- In the S3 console, click your bucket name.
- Select all objects (index.html, style.css, 404.html).
- Click Delete.
- Type permanently delete in the confirmation field and click Delete objects.
Delete the S3 bucket:
- Return to the S3 bucket list.
- Select your bucket and click Delete.
- Enter your bucket name to confirm and click Delete bucket.
Delete the IAM user:
- Navigate to IAM by typing IAM in the console search bar.
- Click Users in the left sidebar.
- Select website-uploader and click Delete.
- Type the username website-uploader to confirm and click Delete user.
Delete the IAM policy:
- In the IAM console, click Policies in the left sidebar.
- Search for S3WebsiteUploaderPolicy.
- Select the policy, click Actions, then Delete.
- Type the policy name to confirm and click Delete.
Nice Work!
Nice Work!
Congratulations! You have deployed your first website to the cloud and secured it with IAM policies.
You now have a live static website anyone can visit, backed by a security model that proves you understand how access control works in AWS.
You've learned how to:
- Created an S3 bucket configured for static website hosting and applied a bucket policy granting public read access with s3:GetObject.
- Created an IAM user with a custom least-privilege policy scoped to s3:PutObject on a single resource.
- Verified that IAM policies enforce access boundaries by confirming the user can upload but cannot delete or browse other buckets.
- Added a custom 404 error page and CSS styling to make your site portfolio-ready.
Ready to quiz yourself?