# Fix AWS S3 Bucket Permission Denied
The dreaded "Access Denied" error when accessing S3 is one of the most common AWS issues. The frustrating part is that S3 doesn't always tell you *which* permission is missing—it just denies access. The problem could be in your IAM policy, the bucket policy, ACLs, or even the bucket's public access settings.
Let me walk you through a systematic approach to track down exactly what's blocking your access.
Understanding S3 Permission Layers
S3 has multiple layers of permissions that all need to align:
- 1.IAM policies attached to users, groups, or roles
- 2.Bucket policies attached to the bucket itself
- 3.Access Control Lists (ACLs) on the bucket or objects
- 4.Public Access Block settings that can override other permissions
- 5.Object ownership settings
If any one of these layers denies access, the request fails. The default is deny—everything must explicitly allow.
Diagnosis Commands
Start by identifying who you're authenticated as:
aws sts get-caller-identityThis tells you the account, user ID, and ARN of the caller. Make sure this matches what you expect.
Now try to list the bucket contents:
aws s3 ls s3://my-bucket-name/If you get "Access Denied," try with the --debug flag to see more details:
aws s3 ls s3://my-bucket-name/ --debug 2>&1 | grep -i "denied\|forbidden\|403"Check if you can read a specific object:
aws s3api head-object \
--bucket my-bucket-name \
--key path/to/object.txtIf the bucket exists but you can't access it, check your effective permissions using the policy simulator:
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/my-user \
--action-names s3:GetObject,s3:ListBucket \
--resource-arns arn:aws:s3:::my-bucket-name,arn:aws:s3:::my-bucket-name/*Check the bucket's public access block settings:
aws s3api get-public-access-block \
--bucket my-bucket-nameIf this command fails with "NoSuchPublicAccessBlockConfiguration," no block is applied. Otherwise, you'll see which restrictions are in place.
Examine the bucket policy:
aws s3api get-bucket-policy \
--bucket my-bucket-name \
--query Policy \
--output text | jq .If you can't read the bucket policy due to permission issues, check if you have s3:GetBucketPolicy permission in your IAM policy.
Check the bucket ACL:
aws s3api get-bucket-acl \
--bucket my-bucket-nameAnd check object-level ACLs:
aws s3api get-object-acl \
--bucket my-bucket-name \
--key path/to/object.txtCommon Causes and Solutions
Missing IAM Policy Permissions
The most common issue is simply not having the right permissions in your IAM policy. Add the required actions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::my-bucket-name",
"arn:aws:s3:::my-bucket-name/*"
]
}
]
}Apply the policy:
aws iam put-user-policy \
--user-name my-user \
--policy-name S3AccessPolicy \
--policy-document file://s3-policy.jsonRemember that s3:ListBucket requires the bucket ARN (without /*), while s3:GetObject and s3:PutObject require the object ARN (with /*).
Restrictive Bucket Policy
The bucket policy might be explicitly denying your access. Look for Deny statements:
aws s3api get-bucket-policy \
--bucket my-bucket-name \
--query Policy \
--output text | jq '.Statement[] | select(.Effect=="Deny")'- 1.If you find a restrictive policy, you'll need to either:
- 2.Have the bucket owner modify the policy
- 3.Access the bucket from a different account or role that's allowed
- 4.Use a bucket owner account with full access
Here's an example of a bucket policy that grants access to a specific user:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowUserAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:user/my-user"
},
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-bucket-name",
"arn:aws:s3:::my-bucket-name/*"
]
}
]
}Apply it:
aws s3api put-bucket-policy \
--bucket my-bucket-name \
--policy file://bucket-policy.jsonCross-Account Access Issues
If you're accessing a bucket in another AWS account, both sides need to grant permission:
- 1.The bucket policy must allow your account
- 2.Your IAM policy must allow access to the bucket
Bucket policy in Account B (bucket owner):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ACCOUNT-A-ID:root"
},
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::bucket-in-account-b",
"arn:aws:s3:::bucket-in-account-b/*"
]
}
]
}IAM policy in Account A (your account):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::bucket-in-account-b",
"arn:aws:s3:::bucket-in-account-b/*"
]
}
]
}Public Access Block Interference
If you're trying to make a bucket or object public and getting access denied, check the public access block settings:
aws s3api get-public-access-block \
--bucket my-bucket-nameIf any of these are enabled, they prevent public access:
- BlockPublicAcls
- IgnorePublicAcls
- BlockPublicPolicy
- RestrictPublicBuckets
Disable them if you truly need public access:
aws s3api put-public-access-block \
--bucket my-bucket-name \
--public-access-block-configuration "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"Object Ownership Issues
With S3 Object Ownership, the bucket owner automatically owns objects written by other accounts. Check the ownership setting:
aws s3api get-bucket-ownership-controls \
--bucket my-bucket-nameIf set to BucketOwnerEnforced, ACLs are disabled and the bucket owner owns all objects. This is generally the recommended setting but can cause issues with legacy cross-account setups.
Wrong Region
Sometimes you get access denied when the bucket is in a different region than your client is configured for. Always specify the region:
aws s3 ls s3://my-bucket-name/ --region us-west-2Verification Steps
After making changes, verify your access works:
```bash # Test listing aws s3 ls s3://my-bucket-name/
# Test reading aws s3api get-object \ --bucket my-bucket-name \ --key test-object.txt \ /dev/null
# Test writing (if needed) echo "test" | aws s3 cp - s3://my-bucket-name/test-write.txt
# Clean up test file aws s3 rm s3://my-bucket-name/test-write.txt ```
For ongoing access issues, enable S3 server access logging to see exactly what's being denied:
aws s3api put-bucket-logging \
--bucket my-bucket-name \
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "my-logs-bucket",
"TargetPrefix": "s3-access-logs/"
}
}'Then analyze the logs for patterns in denied requests.