Amazon S3 bucket returning 403 Forbidden
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
A 403 Forbidden response from Amazon S3 usually means the request reached the bucket but S3 refused to authorize it. The hard part is that several different controls can cause the same HTTP status, so fixing it requires identifying which policy layer or request detail actually denied access.
The Most Common Causes
S3 403 errors usually come from one of these categories:
- missing IAM permissions
- bucket policy deny statements
- block public access settings
- incorrect request signing or expired credentials
- object ownership or cross-account access issues
- missing
s3:ListBucketwhen listing or certain path checks are expected
The error code is the same, but the fix depends on which layer rejected the request.
Start with IAM and Bucket Policy
The first place to look is whether the principal is allowed to perform the requested action.
A minimal allow policy for reading objects might look like this:
If the request lists objects, you also need bucket-level permission such as s3:ListBucket on the bucket ARN itself.
A bucket policy can also override expectations with an explicit deny, and explicit deny wins over allow.
Public Access and Block Public Access
A lot of 403 confusion comes from public-access assumptions. Even if a bucket policy appears to allow public reads, S3 Block Public Access settings at the bucket or account level can still prevent the request.
That means:
- a "looks public" policy may still return
403 - turning on website-style access requires more than just object upload
- anonymous access should be assumed blocked until proven otherwise
If you expect a public URL to work, inspect Block Public Access settings before you keep editing policies blindly.
Pre-Signed URLs Can Also Return 403
A pre-signed URL can return 403 when:
- it is expired
- it was signed for the wrong region or bucket
- headers do not match the signed request
- the object key is wrong
A small Python example that generates a valid pre-signed URL:
If a URL generated like this still fails, verify the bucket region and the object's actual key first.
Cross-Account and Ownership Issues
S3 ownership rules can also lead to 403 responses. This often shows up when:
- one account uploads the object
- another account owns the bucket
- the bucket policy or object ownership settings are not aligned
If object ownership and bucket-owner-enforced controls are involved, the request may look valid from one side and still be rejected from the other.
That is why cross-account cases need both IAM review and bucket-side review.
Use the CLI to Narrow the Problem
The AWS CLI is useful because it gives a clean reproduction path.
If this command returns 403, the issue is usually permission-related rather than a browser-specific problem.
You can also test bucket listing separately:
If head-object works but listing fails, that points strongly toward missing s3:ListBucket rather than missing s3:GetObject.
Region and Endpoint Mismatch
Sometimes a signed request fails because it was sent to the wrong region endpoint. S3 request signing includes region-sensitive details, so signing for one region and sending to another can produce access-style failures that look like generic permission problems.
If you are using SDKs, ensure the client region matches the actual bucket region.
A Practical Debugging Order
A good debugging sequence is:
- identify the exact action that failed such as
GetObjectorListBucket - test it with the AWS CLI
- inspect IAM policy for the caller
- inspect bucket policy for explicit denies
- inspect Block Public Access settings
- confirm region, object key, and ownership details
This is much faster than randomly editing JSON policies.
Common Pitfalls
A common mistake is granting s3:GetObject and forgetting that some workflows also require s3:ListBucket.
Another issue is ignoring explicit deny statements. A single deny in a bucket policy or service-control layer overrides any number of allows.
Developers also often assume a public bucket policy is enough while Block Public Access is still enabled.
Finally, in pre-signed URL cases, do not forget expiration and region. Not every 403 is a pure permission-policy problem.
Summary
- S3
403 Forbiddenusually means authorization or signing failed, not that the bucket is missing. - Check IAM, bucket policy, and Block Public Access first.
- Distinguish
GetObjectfailures fromListBucketfailures. - Pre-signed URLs can fail because of expiration, region mismatch, or wrong keys.
- Debug with a specific action path instead of editing policies at random.

