Symptoms
AccessDenied (403)error when attempting to list, read, or write objects in an S3 bucket.curloraws s3 cpcommands fail withAn error occurred (AccessDenied) when calling the PutObject operation.
Root Cause
The request lacks the necessary permissions. This is typically due to one of the following:
- IAM policy does not grant
s3:GetObject,s3:PutObject, ors3:ListBucketactions. - S3 bucket policy explicitly denies the request or does not allow the principal.
- S3 Block Public Access settings are blocking the request (even for authenticated users if the bucket policy is public).
- Incorrect resource ARN in the policy (e.g., missing
/*for objects). - Pre-signed URL is expired or signed with wrong credentials.
Resolution (Step-by-Step)
-
Identify the caller and action. Check CloudTrail or the error message for the exact
userArnandaction(e.g.,s3:GetObject).aws sts get-caller-identity -
Verify IAM permissions. Ensure the IAM user/role has the required actions.
{ "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"], "Resource": ["arn:aws:s3:::your-bucket", "arn:aws:s3:::your-bucket/*"] } -
Check S3 bucket policy. Use the AWS CLI to retrieve and inspect the policy.
aws s3api get-bucket-policy --bucket your-bucket --query Policy --output text | jq .Look for explicit
Denystatements or missingAllowfor the principal. -
Review S3 Block Public Access settings. If the bucket policy allows public access, these settings can override it.
aws s3api get-public-access-block --bucket your-bucketIf
BlockPublicPolicyistrue, you cannot set a public bucket policy. Set it tofalseif needed:aws s3api put-public-access-block --bucket your-bucket --public-access-block-configuration BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false -
Validate pre-signed URLs. If using pre-signed URLs, ensure they are generated with the correct IAM user and within the expiration window.
aws s3 presign s3://your-bucket/object.txt --expires-in 3600
Verification
Attempt the original operation again. Use the AWS CLI with debug output to confirm the request is allowed:
aws s3 ls s3://your-bucket/ --debug 2>&1 | grep -i "accessdenied\|allow\|deny"
A successful response will show the object list or a 200 OK status.