403 Forbidden

S3 Bucket 403 Forbidden On Public File: The Real Fix

Server & Cloud Intermediate 👁 7 views 📅 Jun 19, 2026

Your S3 object is set to public, but you're still hitting a 403. The problem is almost always the bucket policy or ACL blocking anonymous access.

Quick Answer

If your S3 object is publicly readable but returns 403, check that the bucket's public access block settings aren't overriding it, and verify the bucket policy grants s3:GetObject to * (or the specific IAM role).

Why This Happens

You uploaded a file to S3. You set it to public. You clicked the object URL — and you get a 403 Forbidden. This is one of the most common head-scratchers for developers and sysadmins. I've seen it happen to people who've been using AWS for years. The root cause is usually one of three things, and they're easy to miss because AWS changed its default public access settings in late 2020. Before that, S3 buckets allowed public access by default. Now, every new bucket blocks public access at the account and bucket level unless you explicitly turn those blocks off. So even if you make an object public, the bucket itself says no.

The second most common culprit is a bucket policy that explicitly denies anonymous access. Some AWS services or IAM roles will still get a 403 because the policy is too restrictive. And the third issue is the wrong URL format — you might think you're hitting the object, but you're hitting the bucket root.

The Fix: Step-by-Step

  1. Open the S3 console in the AWS Management Console.
  2. Find your bucket and click on its name.
  3. Click the Permissions tab — this is where all the blocking settings live.
  4. Check the Block Public Access section. If you see a yellow banner saying "Public access is currently blocked", click Edit. Uncheck all four boxes. Then click Save changes. After saving, you should see a green bar saying "Public access is currently allowed." This is the first thing to fix because if this is on, nothing else matters.
  5. Now check the Bucket Policy. In the same Permissions tab, scroll down to Bucket Policy. If there's no policy, you need to add one. If there's a policy, look for a Principal: \"*\" statement that allows s3:GetObject. If it's missing, click Edit and paste this:
{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [{\n    \"Effect\": \"Allow\",\n    \"Principal\": \"*\",\n    \"Action\": \"s3:GetObject\",\n    \"Resource\": \"arn:aws:s3:::your-bucket-name/*\"\n  }]\n}

Replace your-bucket-name with your actual bucket name. Click Save. You should see a confirmation that the policy was updated.

  1. Check the ACL (Access Control List). In the same Permissions tab, click on Access Control List (ACL). If your bucket uses ACLs, you need to grant Everyone (public access) the Read permission for objects. Click Edit, add a grantee for Everyone, check Read, and save. After saving, the ACL should show "Everyone (public access): Read".
  2. Test the URL again. It should now show the object content instead of a 403.

If The Main Fix Doesn't Work

Sometimes the above steps aren't enough. Try these alternatives:

  • Check the object's own ACL. Click on the object name in the bucket, then go to the Permissions tab for that object. Make sure Everyone (public access) has Read permission. If it's not there, add it. This happens when you upload via the AWS CLI with the default private ACL.
  • Use a pre-signed URL. Generate a pre-signed URL from the AWS CLI with aws s3 presign s3://your-bucket/your-file --expires-in 3600. If this works but the plain URL doesn't, your bucket policy or ACL is still blocking anonymous access. The pre-signed URL bypasses the public block because it uses your credentials.
  • Check for an explicit deny in the bucket policy. Some policies have a Deny statement that overrides your Allow. Look for \"Effect\": \"Deny\" and remove it.
  • Verify the URL format. The public URL should be https://your-bucket.s3.amazonaws.com/your-file or https://s3.amazonaws.com/your-bucket/your-file. If you're using a regional endpoint like s3-us-west-2, make sure the region matches your bucket's region.

Prevention Tip

To avoid this in the future, always do this after creating a new bucket: 1) Turn off all public access blocks, 2) Apply the bucket policy above, 3) Set the default ACL to public-read for objects. You can do this in the bucket's Properties tab under Default encryption — no, that's not related. Actually, set it in the bucket's Permissions tab under Object Ownership. Set it to Bucket owner preferred and then in ACL, grant public read. Then as you upload files, they'll inherit public read. But remember: this only works if the bucket policy allows it. The combination of bucket policy + ACL + public access block is what controls this. Miss one, and you'll get a 403.

Was this solution helpful?