Host a Static Website on S3

Learn to host a static website on S3 with this hands-on AWS tutorial. We'll cover the core concept, step-by-step setup, practical walkthrough, troubleshooting, and what to learn next.

Focus: host a static website on s3

Sponsored

You've built a beautiful static site — maybe a landing page, a portfolio, or documentation for your Python project — and now you need to put it on the internet. You could spin up an EC2 instance, install nginx, and babysit a server, but that's overkill and costs money 24/7. The pain is real: you want something fast, cheap, and reliable, without managing infrastructure. The solution is hosting a static website on Amazon S3 — a service that stores your files and serves them to the world with incredible durability and near-zero cost. In this lesson, you'll learn exactly how to do it, step by step, and you'll have your site live by the end.

The problem this lesson solves

Traditional web hosting requires a server. A server means an operating system, security patches, a web server like Apache or nginx, and someone to keep it running. For a static website — where every visitor sees the same HTML, CSS, and JavaScript files — that's a massive waste of time and money. You pay for compute you don't need, and you worry about uptime, DDoS attacks, and scaling. Hosting a static website on S3 eliminates all that: S3 stores your files as objects, serves them over HTTP, and scales automatically to any number of visitors. You don't provision anything; you just upload files and configure a few settings.

Core concept / mental model

Think of S3 as a giant, internet-connected hard drive. Each bucket is a folder, and each file inside is an object. Normally, objects are private and accessed via APIs, but you can turn on static website hosting for a bucket, which makes it behave like a simple web server. When you configure the bucket for web hosting, S3 gives you a website endpoint like http://my-bucket.s3-website-us-east-1.amazonaws.com. Any object you upload becomes a publicly accessible web page, and S3 automatically looks for an index.html file to serve as the homepage.

The bucket itself acts as the root directory. The key insight: you're not running a server; S3 is the server. It handles HTTP requests, serves your files, and returns error documents (like a custom 404.html). The only 'configuration' you do is enabling the feature and setting permissions — the rest is magic.

How it works step by step

  1. Create a bucket — Choose a globally unique name, select a region (e.g., us-east-1), and keep all other defaults. The bucket name will appear in your URL, so pick something readable.
  2. Upload your website files — These are the HTML, CSS, JS, images, etc. You can upload them one at a time or use the AWS CLI to sync a local folder.
  3. Enable static website hosting — In the bucket's Properties tab, configure the index document (e.g., index.html) and error document (e.g., 404.html).
  4. Set public access permissions — By default, S3 blocks public access. You need to disable 'Block all public access' and attach a bucket policy that gives s3:GetObject permission to everyone.
  5. Test your endpoint — S3 provides a website endpoint; visit it in a browser to confirm your site loads.
  6. (Optional) Add a custom domain — Use Route 53 or another DNS provider to point your domain to the S3 endpoint.

Hands-on walkthrough

Let's make it real. You'll need an AWS account and the AWS CLI configured. We'll create a simple portfolio site with two files and host it.

Step 1: Create a simple website

First, create two files locally: index.html and 404.html.

<!-- index.html -->
<!DOCTYPE html>
<html>
<head><title>My S3 Site</title></head>
<body>
<h1>Hello from S3! 🚀</h1>
<p>This page is served from an S3 bucket.</p>
</body>
</html>
<!-- 404.html -->
<!DOCTYPE html>
<html>
<head><title>404 Not Found</title></head>
<body>
<h1>Page not found</h1>
<p>Sorry, this page doesn't exist.</p>
</body>
</html>

Step 2: Create the bucket and upload files using the AWS CLI

Open a terminal and run these commands (replace my-unique-bucket-name with something like my-portfolio-2025):

# Create the bucket (region us-east-1)
aws s3 mb s3://my-unique-bucket-name --region us-east-1

# Upload the files
aws s3 cp index.html s3://my-unique-bucket-name/
aws s3 cp 404.html s3://my-unique-bucket-name/

Step 3: Enable static website hosting

Use the AWS CLI to enable the website configuration:

aws s3 website s3://my-unique-bucket-name \
  --index-document index.html \
  --error-document 404.html

Step 4: Set public access

You must (a) disable the bucket's Public Access Block and (b) attach a bucket policy. Here's a policy you can save as policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-unique-bucket-name/*"
    }
  ]
}

Apply it with:

# Disable the public access block (via CLI)
aws s3api put-public-access-block --bucket my-unique-bucket-name \
  --public-access-block-configuration BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false

# Attach the bucket policy
aws s3api put-bucket-policy --bucket my-unique-bucket-name \
  --policy file://policy.json

Step 5: Test it

Find your website endpoint (in the S3 console under Properties → Static website hosting). It should look like http://my-unique-bucket-name.s3-website-us-east-1.amazonaws.com. Open it in a browser — you'll see your index.html page. Try a random path like /nope and you'll get your 404.html.

Pro tip: Use aws s3 sync instead of cp to upload your site folder. It only transfers changed files, which is a huge time-saver when you update content.

Compare options / when to choose what

Hosting a static website on S3 is not the only option. Here's how S3 compares to other common approaches:

Option Best for Pros Cons
S3 static hosting Static sites (HTML/CSS/JS) Cheap, scalable, zero maintenance No server-side scripting; custom HTTPS requires CloudFront
EC2 + nginx Dynamic sites, full control Full control, can run any stack Requires patching, scaling, and 24/7 cost
CloudFront + S3 Global edge delivery + HTTPS Fast, HTTPS, CDN benefits Slightly more setup, small extra cost

When to choose S3-only: If your site is purely static and you don't need HTTPS (or are okay using the HTTP endpoint for testing), S3 is your best friend. When to add CloudFront: If you need SSL (recommended for any public website) or want to serve content globally faster, put CloudFront in front of S3. You'll learn more about that in a later lesson.

Troubleshooting & edge cases

  • 403 Forbidden on the endpoint: Your bucket policy is missing or the public access block is still enabled. Verify the policy allows s3:GetObject for Principal: "*" and that all four public access block settings are disabled.
  • 404 when accessing the root: Ensure you have an index.html file in the bucket root. S3 won't serve a directory listing; it only serves the index document you specified.
  • Custom domain not resolving: DNS takes time to propagate. Also, the S3 website endpoint must match the region where your bucket lives. For custom domains, you must use a bucket name identical to your domain (e.g., www.example.com) if you're not using CloudFront.
  • HTTPS not available: S3 website endpoints only support HTTP. To get HTTPS, you must use CloudFront (or another CDN). This is a common gotcha.
  • Bucket name already taken: S3 bucket names are globally unique. Choose a more specific name or append a number.

What you learned & what's next

You now understand the core idea behind hosting a static website on S3 and have completed a practical exercise: you created a bucket, uploaded files, enabled website hosting, set permissions, and saw your site live. You also learned when to choose S3 over other options and how to troubleshoot the most common issues.

This is a foundational skill for deploying any static frontend. Next up in the track, we'll explore CloudFront to add a Content Delivery Network, custom domains with HTTPS, and cache behaviors — making your S3-hosted site production-ready. You'll take everything you just built and supercharge it with edge caching and SSL. Stay tuned!

Practice recap

As a hands-on exercise, create a small one-page site for a hypothetical app (like a Python package landing page) and host it on S3 using the CLI. Then, access the website endpoint to verify it works, and try intentionally breaking the policy to see the 403 error — then fix it. This will cement the troubleshooting steps.

Common mistakes

  • Forgetting to disable 'Block all public access' — even with a correct bucket policy, you'll get 403 Forbidden.
  • Creating a bucket with a name that's already taken; S3 names are globally unique, so add a unique suffix.
  • Uploading files but not setting the index document — the site shows 404 at the root.
  • Using the S3 REST API endpoint (e.g., s3.amazonaws.com) instead of the website endpoint — the API doesn't serve HTML content correctly.

Variations

  1. Use the AWS Management Console instead of the CLI — it's a guided point-and-click alternative for visual learners.
  2. Add a CDN (CloudFront) in front of S3 to enable HTTPS and faster global delivery — recommended for production.
  3. Use a static site generator (Hugo, Gatsby, or plain Python with Jinja) to build your site locally, then sync the output to S3.

Real-world use cases

  • Hosting a personal portfolio or resume site with a custom domain and no server costs.
  • Deploying documentation for an open-source Python library (auto-generated with MkDocs) as a versioned S3 site.
  • Serving a static marketing landing page for a startup, with A/B testing via CloudFront and S3.

Key takeaways

  • S3 static website hosting turns a bucket into a simple web server for HTML/CSS/JS with zero compute.
  • You must explicitly enable website hosting and set index/error documents in the bucket properties.
  • Public read access requires both disabling the bucket public access block and attaching a policy allowing s3:GetObject for everyone.
  • The website endpoint is HTTP-only; for HTTPS you need CloudFront.
  • Use aws s3 sync for efficient uploads — it only transfers changed files.
  • S3 is ideal for static sites; choose EC2 for dynamic content and CloudFront for global + HTTPS.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.