AWS S3
static web hosting
403 error
permission denied
troubleshooting

Static web hosting on AWS S3 giving me 403 permission denied

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A 403 Permission Denied response from an S3-hosted static site usually means the website request reached S3, but S3 refused to serve the object. The fix depends on how you are hosting the site, because the correct permissions for public website hosting are different from the permissions used with private buckets behind CloudFront.

Know Which S3 Endpoint You Are Using

This is the first thing to verify. S3 exposes two common access patterns:

  • the static website endpoint, used for public website hosting
  • the REST API endpoint, used for normal object access and often used behind CloudFront

If you enabled static website hosting, test the website endpoint shown in the bucket properties, not just the generic object URL. A common source of confusion is that the REST endpoint and the website endpoint behave differently for directory indexes, redirects, and permissions.

If you request https://bucket.s3.amazonaws.com/ for a site that was meant to be served from the website endpoint, you may see a 403 even though the website endpoint works.

Public Website Hosting Requires Public Read Access

For direct S3 website hosting, the objects must be publicly readable. That means two things must be true:

  • Block Public Access settings cannot prevent the policy
  • the bucket policy must allow s3:GetObject on the website files

A minimal public-read bucket policy looks like this:

json
1{
2  "Version": "2012-10-17",
3  "Statement": [
4    {
5      "Sid": "PublicReadForWebsite",
6      "Effect": "Allow",
7      "Principal": "*",
8      "Action": "s3:GetObject",
9      "Resource": "arn:aws:s3:::my-static-site/*"
10    }
11  ]
12}

You can apply it with the AWS CLI:

bash
aws s3api put-bucket-policy \
  --bucket my-static-site \
  --policy file://bucket-policy.json

If Block Public Access is still enabled for bucket policies, S3 ignores the public-read policy and your site keeps returning 403.

Check the Actual Object Keys

S3 is an object store, not a traditional filesystem. If your website expects index.html at the bucket root but the file was uploaded under a different key, S3 cannot serve it as the index document.

Verify both the website configuration and the uploaded keys:

bash
aws s3api get-bucket-website --bucket my-static-site
aws s3 ls s3://my-static-site/ --recursive

If the index document is set to index.html, make sure that exact object exists at the expected path. A missing index document can surface as a 403 from the website endpoint.

Private Bucket Behind CloudFront Is a Different Setup

If the bucket is supposed to stay private, direct S3 website hosting is not the right model. In that pattern, CloudFront should read from the S3 REST endpoint using an origin access control or an older origin access identity, and the bucket policy should allow only CloudFront rather than the public internet.

That means a 403 can also happen when people mix the two patterns:

  • static website hosting enabled, but bucket kept private
  • CloudFront configured, but user tests S3 directly
  • bucket policy grants public read, but CloudFront origin expects private access

Pick one model and configure permissions accordingly.

A Fast Troubleshooting Sequence

Use a short checklist instead of changing many settings at once:

bash
1aws s3api get-public-access-block --bucket my-static-site
2aws s3api get-bucket-policy --bucket my-static-site
3aws s3api get-bucket-website --bucket my-static-site
4curl -I http://my-static-site.s3-website-us-east-1.amazonaws.com/

That sequence tells you whether public access is blocked, whether a bucket policy exists, whether website hosting is enabled, and what the website endpoint returns.

Common Pitfalls

The most common mistake is enabling static website hosting but leaving Block Public Access enabled, which makes the public bucket policy ineffective.

Another common mistake is using the wrong endpoint during testing. The S3 website endpoint and the object endpoint are not interchangeable for website behavior.

It is also easy to upload files under the wrong prefix or forget the configured index document name. S3 does not infer website structure the way a regular web server might.

Summary

  • A 403 from S3 website hosting usually means S3 reached the bucket but denied access.
  • Check whether you are using the website endpoint or the REST endpoint.
  • For direct S3 website hosting, objects must be publicly readable and Block Public Access must allow that policy.
  • Confirm that index.html and any other referenced files exist under the expected keys.
  • If the bucket should stay private, serve it through CloudFront with the correct private-origin policy instead of public S3 website hosting.

Course illustration
Course illustration

All Rights Reserved.