
Amazon S3 Web Hosting: How to Host a Static Website on AWS S3 (Step by Step)
Published · Updated
Short answer. S3 web hosting means turning on static website hosting for an Amazon S3 bucket, uploading your HTML, CSS, JavaScript and images, and allowing public read access. S3 then serves the site at a website endpoint such as http://your-bucket.s3-website.ap-south-1.amazonaws.com. There is no server to run. The website endpoint speaks HTTP only, so for HTTPS or a private bucket you put Amazon CloudFront (or AWS Amplify Hosting) in front of it, which Part 4 covers.
Part 1 of 4 — static hosting on AWS, end to end:
- Host it on S3 (you are here)
- Add your own domain with Route 53
- Automate deploys with GitHub Actions
- Lock it down with CloudFront and a private bucket
What is Amazon S3 web hosting?
Amazon S3 is object storage. When you enable static website hosting on a bucket, S3 also answers browser requests for those objects: a request for the site root returns your index document, a missing page returns your error document, and every other path returns the matching file. AWS describes a static site as one whose pages “include static content” and “might also contain client-side scripts” (Hosting a static website using Amazon S3).
That makes S3 a good fit for:
- a portfolio or résumé site,
- a landing page or documentation site,
- the built output of a static site generator, or of a React, Vue or Angular app (the
dist/orbuild/folder).
It is not a fit for anything that runs code on the server per request, such as PHP, a Node.js or Spring Boot backend, or a database. S3 serves files; it does not execute them. For a server-side app, see deploying Spring Boot on EC2 behind a load balancer.
Before you start
You need:
- an AWS account and access to the S3 console,
- your site’s files in one folder, with the home page named exactly
index.html(the name is case sensitive, soIndex.htmlwill not work), - optionally a
404.htmlpage for missing URLs.
If you use a build tool, upload the build output, not your source folder.
Step 1: Create the bucket
- Open the S3 console and choose Create bucket.
- Enter a bucket name. Names are global across AWS, so a common word is usually taken. Use lowercase letters, numbers, dots and hyphens.
- Choose an AWS Region close to your visitors. The Region becomes part of your website endpoint.
- Leave Object Ownership at its default, ACLs disabled (Bucket owner enforced).
- Leave Block all public access turned on for now. You will change it deliberately in Step 3, once you know exactly what it does.
- Choose Create bucket.
Planning to use your own domain later? If you will point a domain at the S3 website endpoint, the bucket name must match the domain name, for example a bucket named www.example.com for the site www.example.com (custom domain walkthrough). Choose that name now; a bucket cannot be renamed. Part 2 covers the domain setup.
Step 2: Turn on static website hosting
- Open the bucket and choose the Properties tab.
- Scroll to Static website hosting and choose Edit.
- Choose Enable, and choose to host a static website (the other option redirects requests to another host).
- Set Index document to
index.html. - Optionally set Error document to
404.html. S3 returns it for 4XX errors; without one, S3 returns its own plain HTML error page. - Choose Save changes.
At the bottom of the Properties tab you now see the Bucket website endpoint. Depending on the Region it uses one of two formats (Website endpoints):
http://bucket-name.s3-website-region.amazonaws.com
http://bucket-name.s3-website.region.amazonaws.com
Opening it now returns 403 Forbidden. That is expected: the bucket is still private.
Step 3: Edit S3 Block Public Access
New buckets block all public access by default, and that setting overrides any policy that tries to make objects public. To serve the site straight from the website endpoint you must turn it off for this bucket:
- Choose the Permissions tab.
- Under Block public access (bucket settings), choose Edit.
- Clear Block all public access, choose Save changes and confirm.
There is also an account-level Block Public Access setting. S3 applies the most restrictive combination of the two, so if the account setting is on, your bucket stays private whatever you do here (Setting permissions for website access). If the console shows a note about account settings under this section, that is the cause.
Only do this for content that is meant to be public. Anyone on the internet can read every object the policy in Step 4 covers. Never put backups, .env files or anything private in a website bucket. If you would rather keep Block Public Access on, skip to the HTTPS section and use CloudFront with origin access control instead.
Step 4: Add a bucket policy for public read
A bucket policy grants read access to every object in the bucket at once. This is AWS’s own example (Setting permissions for website access). Replace your-bucket-name with your bucket’s name:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::your-bucket-name/*"]
}
]
}
- On the Permissions tab, under Bucket policy, choose Edit.
- Paste the policy, change the bucket name, and choose Save changes.
The policy allows only s3:GetObject, which is reading files. It does not let visitors list, upload or delete anything. The /* at the end of the resource matters: it means “every object in the bucket”, not the bucket itself.
Why not “Make public”? Older tutorials, including an earlier version of this one, suggest selecting objects and choosing Actions → Make public. That uses object ACLs. New buckets have ACLs disabled by default, so the action fails, and AWS recommends keeping them disabled and using a bucket policy instead.
If saving fails with Policy has invalid resource, the bucket name in the policy does not match. If it fails with an access-denied error, Block Public Access is still on at the bucket or account level.
Step 5: Upload your site
- Choose the Objects tab, then Upload.
- Add your files and folders, keeping the same structure as on your computer.
index.htmlmust sit at the top level of the bucket, not inside a folder named after your project. - Choose Upload.
Two details catch people out:
- Subfolders need their own index document. A request for
/blog/returnsblog/index.html. A request for/blogwithout the slash makes S3 look for an object namedblogfirst, then redirect to/blog/ifblog/index.htmlexists (Configuring an index document). - File names are case sensitive.
Logo.PNGandlogo.pngare different objects, even if your laptop treats them as the same.
Prefer the command line? With the AWS CLI configured, this uploads the folder and removes files you have deleted locally:
aws s3 sync ./site s3://your-bucket-name --delete
Part 3 runs that same upload from GitHub Actions on every push.
Step 6: Test the website endpoint
Open the Bucket website endpoint from the Properties tab in a private browser window. You should see your home page. Then check:
- a page inside a folder,
- a URL that does not exist, which should show your
404.html, - images and CSS, by opening the browser’s developer tools and looking for failed requests.
Use the website endpoint, not the object URL shown when you click a file (https://your-bucket-name.s3.region.amazonaws.com/index.html). The object URL is the REST endpoint: it does not serve index documents or your error page, and it returns XML errors.
S3 website endpoint vs CloudFront vs Amplify Hosting
The steps above give you the plainest form of S3 web hosting. It works, but it has limits that matter once real visitors arrive. AWS lists the differences between the website endpoint and the REST endpoint in its endpoint comparison:
| S3 website endpoint (this guide) | CloudFront + private bucket with OAC | AWS Amplify Hosting | |
|---|---|---|---|
| HTTPS | No, HTTP only | Yes | Yes |
| Bucket must be public | Yes | No, Block Public Access stays on | No |
| Custom domain | Yes, HTTP only (Part 2) | Yes, with an ACM certificate | Yes |
| Caching close to visitors | No | Yes, CloudFront edge locations | Yes, on CloudFront |
| Index and error documents | Built in | Default root object; error pages through CloudFront settings | Built in |
| Best for | Learning, internal demos, quick tests | Production sites you want to control in detail | Production sites with the least setup |
AWS’s own S3 documentation now recommends Amplify Hosting for static sites stored in S3, because it puts the site on CloudFront and gives you an HTTPS URL without extra configuration (Website endpoints).
HTTPS and a private bucket: CloudFront with origin access control
The S3 website endpoint does not support HTTPS. To serve the site over HTTPS, the usual pattern is:
- Keep the bucket private, with Block Public Access on and no public bucket policy.
- Create a CloudFront distribution whose origin is the bucket’s REST endpoint, not the website endpoint.
- Attach an origin access control (OAC) so that only that distribution can read the bucket. The bucket policy then allows the
cloudfront.amazonaws.comservice principal, limited by anAWS:SourceArncondition to your distribution (Restrict access to an Amazon S3 origin). - Set the distribution’s default root object to
index.html, and attach an ACM certificate for your domain.
One point people miss: OAC cannot be used with a website endpoint origin. CloudFront treats a website endpoint as a custom origin, and AWS states that OAC (and the older OAI) does not apply to it. You choose one model or the other: a public bucket with the website endpoint, or a private bucket behind CloudFront with OAC. AWS recommends OAC over the legacy origin access identity (OAI).
If your bucket uses SSE-KMS encryption, the website endpoint cannot serve it at all, because anonymous users cannot use KMS keys. CloudFront with OAC is required in that case (Hosting a static website using Amazon S3).
Part 4 walks through this setup end to end, including Route 53 and the certificate.
Common S3 static website errors and fixes
| What you see | Usual cause | Fix |
|---|---|---|
403 Forbidden on every page |
Block Public Access still on, or no bucket policy | Repeat Steps 3 and 4; check the account-level setting too |
404 Not Found on the home page |
No index.html at the bucket root, or a different case |
Upload index.html to the top level, not inside a folder |
| Raw XML error instead of your page | You opened the object URL (REST endpoint) | Use the bucket website endpoint |
| Page downloads instead of displaying | The file was uploaded with the wrong Content-Type |
Re-upload through the console or aws s3 sync, which set it from the file extension |
| Styles or images missing | Paths wrong after upload, or case mismatch | Check the network tab; compare paths and letter case with the bucket |
NoSuchBucket |
A typo in the endpoint, or the bucket was deleted | Copy the endpoint from the Properties tab |
| Browser says “Not secure” | The website endpoint is HTTP only | Use CloudFront or Amplify Hosting for HTTPS |
AWS documents that the website endpoint returns 404 Not Found for an object that does not exist and 403 Access Denied for one that exists but is not readable, and notes that this lets a visitor infer which object names exist (Setting permissions for website access).
Costs and cleanup
S3 static hosting has no server to pay for, but it is not free. You pay for the storage your files use, the requests visitors make and the data transferred out to them, and CloudFront adds its own charges if you use it. New accounts may have free-tier coverage. Check the current Amazon S3 pricing page for your Region rather than relying on a number in any tutorial.
When a practice site is finished:
- Empty the bucket (the console’s Empty button).
- Delete the bucket.
- Delete any CloudFront distribution, Route 53 records or certificates you created for it.
Deleting the bucket frees the name, but someone else can then claim it.
S3 web hosting FAQ
Can Amazon S3 host a website?
Yes, a static one. S3 can serve HTML, CSS, JavaScript, images and other files directly to browsers once static website hosting is enabled. It cannot run server-side code such as PHP, Node.js or Java.
Does S3 static website hosting support HTTPS?
Not on its own. The S3 website endpoint supports HTTP only. For HTTPS, put Amazon CloudFront in front of the bucket, or use AWS Amplify Hosting, which AWS recommends for static sites stored in S3.
Do I have to make my bucket public?
Only if visitors use the S3 website endpoint directly. With CloudFront and origin access control, the bucket stays private and only CloudFront can read it. That is the safer choice for production.
Can I host a React, Angular or Vue app on S3?
Yes. Run the production build and upload the output folder. Single-page apps that use client-side routing need every unknown path to return index.html. Setting the error document to index.html makes the page load, but S3 still sends a 404 status with it. With CloudFront you can map those errors to index.html with a 200 response instead.
What is the difference between the S3 website endpoint and the object URL?
The website endpoint (s3-website in the hostname) behaves like a web server: it returns index and error documents, supports redirects, and serves only public content over HTTP. The object URL is the REST API endpoint: it supports HTTPS and private content but returns raw files and XML errors, and does not serve index documents.
Can I use my own domain with S3 hosting?
Yes. Name the bucket after the domain, then create an alias record in Route 53 that points to the website endpoint. Part 2 shows the steps. For HTTPS on your domain, use CloudFront, as in Part 4.
How do I update the site after changes?
Upload the changed files again; S3 serves the new version straight away. Behind CloudFront, cached copies can stay until they expire unless you create an invalidation. To deploy on every push, use GitHub Actions.
Is S3 static website hosting free?
No. You pay for storage, requests and data transfer, and new accounts may have free-tier usage. Confirm on the S3 pricing page and delete practice buckets when you finish.
Next step
Your site is live over HTTP. Continue to Part 2: add your own domain with Route 53, or go straight to Part 4 for HTTPS with a private bucket. Hosting, DNS, CDN and access control like this are core cloud architecture skills in the Cloud/AI Solutions Architect program.