AWS S3 Bucket Policy Blocks Static Site — 403 Forbidden Fix
Your S3 static site returns 403 when the bucket policy blocks public read. The fix is adding the right policy statement or enabling correct ACLs.
You set up an S3 bucket for static website hosting. You uploaded your files. You enabled Static Website Hosting in the Properties tab. But when you hit the endpoint — something like http://my-bucket.s3-website-us-east-1.amazonaws.com — you get a plain 403 Forbidden. No index.html loading. No error page. Just a brick wall.
This happens most often right after you create a new bucket or tweak the bucket policy. The culprit here is almost always the bucket policy blocking public read access. AWS tightened default permissions a few years back, so new buckets start with all public access blocked. If you try to serve a static site without explicitly allowing anonymous s3:GetObject, you'll hit this.
Root Cause
An S3 bucket policy is an IAM-like document that controls access at the bucket level. For a static website, you need to allow Principal: "*" (anonymous users) to read objects. If that statement is missing, or if the policy has a Deny that overrides it, S3 returns 403. The same thing happens if the bucket's Block Public Access settings are still enabled. Those settings override any policy.
Don't confuse this with file permissions — ACLs on individual objects won't fix a bucket-level policy block. And don't bother checking your IAM user permissions; this is about the bucket itself, not your account.
The Fix
Do these steps in order. Skip around and you'll waste time.
- Disable Block Public Access — This is the first thing to check. In the S3 console, go to your bucket → Permissions → Block Public Access. Click Edit and uncheck all four boxes. Save. If you're in a security-sensitive environment, you can keep "Block public access to buckets and objects through ACLs" checked, but you must allow public policies.
- Set the bucket policy — Still in Permissions, under Bucket Policy, paste this exact policy. Replace
your-bucket-namewith your actual bucket name.
Make sure the{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::your-bucket-name/*" } ] }Resourceends with/*— that gives access to all objects inside the bucket. Without the/*, you're only granting access to the bucket itself, not the files. - Verify the website endpoint — Wait 30 seconds for the policy to propagate. Then open
http://your-bucket-name.s3-website-region.amazonaws.comin a private browser window. You should see your index page. - Double-check the index document — If you still get 403, confirm your Static Website Hosting settings point to the right file. Under Properties → Static Website Hosting, the Index Document should be
index.html(or whatever you named it). Also set an Error Document like404.html— it won't fix the 403, but it helps debugging.
Still Failing? Check These
If you've done all that and it's still 403, you've got one of these edge cases:
- Bucket Policy Deny statement — Someone added a deny rule. Look for any
Effect": "Deny"in your policy that applies tos3:GetObject. Remove it. - CloudFront in front of S3 — If you're using CloudFront, the 403 might come from CloudFront, not S3. Check your CloudFront distribution's Origin Access Identity (OAI) settings. S3 bucket policy must allow the OAI, not public access.
- Region-specific endpoint — Make sure you're using the right website endpoint. It's
s3-website-followed by the region code, nots3.. For example,s3-website-us-west-2.amazonaws.com, nots3-us-west-2.amazonaws.com. The wrong endpoint gives a different error, but double-check anyway. - Bucket name mismatch — The bucket name in the policy's
Resourcemust match exactly, including hyphens and dots. Case matters. S3 bucket names are globally unique, so copy-paste from the console.
One last thing: if you enabled ACLs on the bucket, you might have a conflict. For static sites, skip ACLs entirely. Use the bucket policy above and set object ACLs to "None" (the default). Mixing ACLs and policies is a headache you don't need.
That's it. The 403 should be gone. If not, drop the bucket policy, re-check Block Public Access, and try again. Nine times out of ten, it's one of those two.
Was this solution helpful?