What's Actually Happening

S3 returns 'Access Denied' (403 Forbidden) when you attempt to upload an object, even though you believe you have the correct permissions. The error can come from multiple layers: IAM policies, bucket policies, ACLs, encryption settings, or account-level blocks.

The Error You'll See

Using AWS CLI:

bash
$ aws s3 cp file.txt s3://my-bucket/
upload failed: ./file.txt to s3://my-bucket/file.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

Using SDK:

bash
botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

Why This Happens

  1. 1.Missing IAM permissions - The IAM user/role lacks s3:PutObject permission
  2. 2.Bucket policy blocking access - Explicit deny in bucket policy
  3. 3.KMS key policy - If bucket uses SSE-KMS, you need kms:GenerateDataKey permission
  4. 4.Object ownership - Bucket has ACLs disabled or different owner
  5. 5.Public access block - Account-level block on public buckets being applied incorrectly
  6. 6.VPC endpoint policy - Restrictive policy on S3 VPC endpoint
  7. 7.Object Lock - Bucket has Object Lock enabled and you're overwriting a locked object

Step 1: Verify IAM Permissions

Check the effective permissions for your identity:

```bash # Simulate the PutObject action aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::123456789012:user/myuser \ --action-names s3:PutObject \ --resource-arns arn:aws:s3:::my-bucket/*

# Check attached policies aws iam list-attached-user-policies --user-name myuser aws iam list-user-policies --user-name myuser ```

Required IAM permission:

json
{
  "Effect": "Allow",
  "Action": "s3:PutObject",
  "Resource": "arn:aws:s3:::my-bucket/*"
}

Step 2: Check Bucket Policy

bash
aws s3api get-bucket-policy --bucket my-bucket --output json

Look for explicit denies or conditions that might block your upload:

json
{
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:PutObject",
  "Resource": "arn:aws:s3:::my-bucket/*",
  "Condition": {
    "StringNotEquals": {
      "aws:PrincipalArn": "arn:aws:iam::123456789012:role/AllowedRole"
    }
  }
}

An explicit deny always overrides any allow.

Step 3: Check KMS Key Permissions

If the bucket uses SSE-KMS encryption:

```bash # Check bucket encryption aws s3api get-bucket-encryption --bucket my-bucket

# Check KMS key policy aws kms get-key-policy --key-id alias/my-key --policy-name default ```

You need both:

json
{
  "Effect": "Allow",
  "Action": [
    "kms:GenerateDataKey",
    "kms:Encrypt"
  ],
  "Resource": "arn:aws:kms:region:account:key/key-id"
}

Step 4: Check Object Ownership

bash
aws s3api get-bucket-ownership-controls --bucket my-bucket

If ObjectOwnership is set to BucketOwnerEnforced, ACLs are disabled and any ACL in your upload request will cause a failure.

Step 5: Test with Minimal Request

Isolate the issue by testing with a minimal upload:

bash
aws s3api put-object --bucket my-bucket --key test.txt --body test.txt

If this works, the issue may be with additional headers like: - x-amz-acl (ACL header) - x-amz-server-side-encryption (encryption header) - x-amz-storage-class (storage class)

Step 6: Check VPC Endpoint Policy

If accessing S3 via VPC endpoint:

bash
aws ec2 describe-vpc-endpoints --filters Name=service-name,Values=com.amazonaws.region.s3

Check the endpoint policy for restrictions on PutObject.

Step 7: Enable S3 Server Access Logging

For persistent issues, enable access logging to see the actual denial reason:

bash
aws s3api put-bucket-logging --bucket my-bucket \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "log-bucket",
      "TargetPrefix": "s3-access-logs/"
    }
  }'

Check logs for the specific denial reason.

Verify the Fix

bash
aws s3 cp test.txt s3://my-bucket/test.txt --debug

Look for the HTTP response code in debug output. A successful upload returns 200.

  • [Fix AWS S3 Bucket Policy Too Restrictive](/articles/fix-aws-s3-bucket-policy-restrictive)
  • [Fix AWS S3 404 NoSuchBucket Error](/articles/fix-aws-s3-nosuchbucket-error)
  • [Fix AWS KMS Access Denied](/articles/fix-aws-kms-access-denied)