AWS S3 Access Denied (403): Comprehensive Troubleshooting Guide
Fix AWS S3 Access Denied (403) errors. Learn how to troubleshoot IAM policies, bucket policies, block public access, and KMS encryption issues for S3 buckets.
- IAM policies or Bucket policies lack the required permissions (e.g., s3:GetObject, s3:ListBucket, s3:PutObject).
- AWS S3 Block Public Access (BPA) settings are overriding bucket policies and ACLs, preventing public access.
- Missing KMS key permissions (kms:Decrypt or kms:GenerateDataKey) when accessing SSE-KMS encrypted objects.
- CloudFront OAI/OAC is misconfigured, leading to access denied errors when serving S3 static websites.
- Quick Fix: Use the AWS Policy Simulator and verify the exact API operation (e.g., ListObjectsV2 vs PutObject) failing in CloudTrail.
| Method | When to Use | Time | Risk |
|---|---|---|---|
| IAM Policy Update | Principal lacks basic S3 permissions | 5 mins | Low |
| Bucket Policy Update | Cross-account access or public access required | 10 mins | Medium |
| Disable Block Public Access | Making a bucket intentionally public (e.g., static assets) | 2 mins | High |
| KMS Key Policy Update | Objects are encrypted with a CMK and decryption fails | 15 mins | Medium |
| CloudFront OAC Setup | Securing a static website behind CloudFront | 20 mins | Low |
Understanding the Error
The Access Denied error in AWS S3 is an HTTP 403 Forbidden response. It occurs when AWS evaluates your request and determines that the calling principal (IAM user, role, or anonymous user) does not have the necessary permissions to perform the requested operation on the specified S3 resource (bucket or object).
You might see this error in various forms depending on the tool or SDK you are using:
fatal error: An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied(often seen withaws s3 lsoraws s3 sync).An error occurred (AccessDenied) when calling the PutObject operation: Access Denied(seen withaws s3 cpor file uploads).<Code>AccessDenied</Code><Message>Access Denied</Message>(raw XML response in a browser or API client).HTTP/1.1 403 Forbidden(in HTTP response headers).
Because AWS IAM evaluates multiple policy types (Identity-based, Resource-based, Permissions Boundaries, SCPs, and Session Policies) alongside S3-specific features (Block Public Access, ACLs, Object Ownership), pinpointing the root cause requires a systematic approach.
Step 1: Verify IAM Identity-Based Policies
The first and most common cause of an S3 Access Denied error is that the IAM user or role executing the command lacks the required permissions.
For example, if you run aws s3 ls s3://my-bucket, the principal needs the s3:ListBucket permission. If you run aws s3 cp file.txt s3://my-bucket/, the principal needs s3:PutObject.
Diagnosis:
- Identify the exact IAM role or user making the request. If you are using the AWS CLI, run
aws sts get-caller-identityto see who you are authenticated as. - Review the IAM policies attached to this principal.
- Ensure the policy explicitly grants the required action and that the
ResourceARN is correct.
Common Mistake: Confusing bucket-level permissions with object-level permissions. s3:ListBucket applies to the bucket (arn:aws:s3:::my-bucket), while s3:GetObject and s3:PutObject apply to the objects within the bucket (arn:aws:s3:::my-bucket/*).
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::my-bucket"]
},
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject"],
"Resource": ["arn:aws:s3:::my-bucket/*"]
}
]
}
Step 2: Check S3 Bucket Policies
Bucket policies are resource-based policies attached directly to the S3 bucket. They can grant or deny permissions across the entire bucket or specific prefixes.
Crucially, in AWS, an explicit Deny always overrides an explicit Allow. If an IAM policy grants access but the bucket policy denies it, the result is Access Denied.
Diagnosis:
- Navigate to the S3 console, select your bucket, and go to the Permissions tab.
- Review the Bucket policy.
- Look for statements with
"Effect": "Deny"that might inadvertently match your IAM principal, IP address, or VPC endpoint. - If you are attempting cross-account access, the bucket policy must explicitly grant access to the external account's principal, AND the external account's IAM policy must also grant access (two-way handshake).
Step 3: S3 Block Public Access (BPA)
AWS S3 Block Public Access is a security feature that can be applied at the account level or the bucket level. It is designed to prevent accidental public exposure of S3 data.
If you are trying to make objects public (e.g., for a static website or public downloads) and you are getting Access Denied, BPA is the likely culprit.
Diagnosis:
- In the S3 console, check the Block public access (bucket settings) under the Permissions tab.
- Also check the account-level settings in the S3 console side navigation (Block Public Access settings for this account).
- If
BlockAllPublicAccessis enabled, any bucket policy or ACL that attempts to grant public access (e.g.,"Principal": "*") will be ignored, resulting in a 403 error for unauthenticated requests.
Fix: If the bucket must be public, you need to turn off the specific BPA settings (typically "Block public access to buckets and objects granted through new public bucket or access point policies" and "Block public and cross-account access to buckets and objects through any public bucket or access point policies").
Step 4: Investigate AWS KMS Encryption
If your S3 bucket uses AWS Key Management Service (SSE-KMS) to encrypt objects, having S3 permissions is not enough. The IAM principal must also have permissions to use the KMS key.
- To upload an object (
PutObject), you needkms:GenerateDataKey. - To download an object (
GetObject), you needkms:Decrypt.
Diagnosis:
If you can run aws s3 ls successfully but aws s3 cp (download) fails with an Access Denied error, KMS is a prime suspect.
Fix: Update the IAM policy or the KMS Key policy to allow the principal to use the key.
{
"Effect": "Allow",
"Action": ["kms:Decrypt", "kms:GenerateDataKey"],
"Resource": "arn:aws:kms:region:account-id:key/key-id"
}
Step 5: Object Ownership and ACLs
Historically, S3 used Access Control Lists (ACLs) to manage permissions. If an object is uploaded by a different AWS account (cross-account upload), the uploading account owns the object by default, not the bucket owner. The bucket owner might get an Access Denied error when trying to read the object.
Fix: AWS recommends disabling ACLs entirely. Go to the bucket's Permissions tab, edit Object Ownership, and set it to Bucket owner enforced. This disables all ACLs, and access is purely governed by IAM and Bucket policies. If you must use ACLs, ensure the uploader includes the bucket-owner-full-control canned ACL during the PutObject request.
Step 6: CloudFront OAI/OAC for S3 Static Websites
When serving a static website from S3 via CloudFront, you should restrict direct access to the S3 bucket and only allow CloudFront to fetch objects. This is done using Origin Access Identity (OAI) or the newer Origin Access Control (OAC).
If you see Access Denied when accessing your CloudFront distribution:
- Ensure the S3 bucket policy allows
s3:GetObjectfor the CloudFront OAC/OAI. - Ensure the requested object actually exists. In S3, if an object doesn't exist (404 Not Found) AND the requester doesn't have
s3:ListBucketpermissions, S3 returns a 403 Access Denied to prevent revealing whether the object exists. - Ensure your CloudFront distribution has a Default Root Object (e.g.,
index.html) configured.
Step 7: Presigned URLs
Presigned URLs allow temporary access to S3 objects. If a presigned URL returns Access Denied:
- Expiration: The URL has passed its expiration time.
- Permissions: The IAM principal that generated the presigned URL must have the necessary permissions (e.g.,
s3:GetObject) at the time the URL is used. If the principal's permissions are revoked after generating the URL, the URL will fail. - Signature Mismatch: The HTTP method used (GET vs PUT) or the headers must match exactly what was signed.
Frequently Asked Questions
#!/bin/bash
# Diagnostic script to check S3 bucket access permissions
BUCKET_NAME="your-target-bucket"
FILE_NAME="test-access-file.txt"
echo "Checking IAM Caller Identity..."
aws sts get-caller-identity
echo "\nTesting s3:ListBucket permission..."
aws s3 ls s3://$BUCKET_NAME/ || echo "FAILED: s3:ListBucket Access Denied"
echo "\nTesting s3:PutObject permission..."
echo "test content" > $FILE_NAME
aws s3 cp $FILE_NAME s3://$BUCKET_NAME/ || echo "FAILED: s3:PutObject Access Denied"
echo "\nTesting s3:GetObject permission..."
aws s3 cp s3://$BUCKET_NAME/$FILE_NAME ./downloaded-$FILE_NAME || echo "FAILED: s3:GetObject Access Denied"
echo "\nFetching Bucket Policy (requires s3:GetBucketPolicy)..."
aws s3api get-bucket-policy --bucket $BUCKET_NAME --query Policy --output text | jq . || echo "No bucket policy or permission denied."
echo "\nChecking Block Public Access settings..."
aws s3api get-public-access-block --bucket $BUCKET_NAME || echo "No BPA settings found or permission denied."
# Cleanup
rm $FILE_NAME ./downloaded-$FILE_NAME 2>/dev/nullError Medic Editorial
Our SRE and Cloud Operations team writes in-depth, battle-tested troubleshooting guides to help developers resolve complex cloud infrastructure issues quickly.
Sources
- https://docs.aws.amazon.com/AmazonS3/latest/userguide/troubleshoot-403-errors.html
- https://repost.aws/knowledge-center/s3-troubleshoot-403
- https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
- https://aws.amazon.com/premiumsupport/knowledge-center/cloudfront-access-denied-errors/