Error Medic

AWS S3 Access Denied (403 Forbidden): A Complete Troubleshooting Guide

Fix AWS S3 Access Denied and 403 Forbidden errors. Learn how to troubleshoot IAM policies, Bucket Policies, KMS keys, and S3 Object Ownership rules.

Last updated:
Last verified:
1,524 words
Key Takeaways
  • S3 Access Denied (403 Forbidden) occurs when explicit 'Allow' permissions are missing or an explicit 'Deny' is active.
  • Check the IAM Policy, Bucket Policy, Block Public Access settings, and KMS Key Policy, as S3 evaluates all of them.
  • Enable 'Bucket owner enforced' to solve cross-account object ownership permission issues.
  • For 503 Slow Down or timeouts due to S3 rate limits, implement exponential backoff and prefix randomization.
S3 Authorization Mechanisms Compared
MechanismWhen to UseScopeCommon Errors
IAM PoliciesGranting permissions to users/roles in your account.Account-wideMissing s3:GetObject or targeting bucket ARN instead of object ARN.
Bucket PoliciesGranting cross-account access or enforcing security rules (e.g., HTTPS only).Bucket-specificAccidental explicit Deny blocking all users including admins.
Block Public Access (BPA)Preventing accidental data exposure globally.Account or BucketOverrides public bucket policies resulting in unexpected 403s.
KMS Key PoliciesWhen objects are encrypted using SSE-KMS.Key-specificMissing kms:Decrypt permissions for the IAM role on the specific key.

Understanding the Error: AWS S3 Access Denied (403 Forbidden)

When you encounter an AccessDenied error (HTTP 403 Forbidden) while interacting with Amazon Simple Storage Service (S3), it means that AWS has evaluated your request and determined that the principal (IAM user, role, or anonymous user) does not have the necessary permissions to perform the requested action on the target resource (bucket or object).

Unlike 404 Not Found, a 403 Forbidden explicitly indicates that the request reached S3 and the resource exists, but authorization failed. S3's authorization model is notoriously complex, relying on a union of multiple policy types that are evaluated together.

The evaluation logic follows a default-deny approach. For a request to be allowed, there must be an explicit Allow in an applicable policy, and absolutely no explicit Deny in any applicable policy. An explicit Deny always overrides an explicit Allow.

The S3 Authorization Evaluation Chain

When troubleshooting aws s3 permission denied issues, you must check the following components in order of common failure:

  1. IAM Policies: Does the user or role making the request have an Identity-based policy granting s3:GetObject, s3:PutObject, or s3:ListBucket?
  2. Bucket Policies: Does the resource-based policy attached to the S3 bucket explicitly allow the principal? Does it have a broad Deny statement (e.g., enforcing SSL, restricting by IP) that is blocking the request?
  3. Block Public Access (BPA): Are account-level or bucket-level Block Public Access settings overriding bucket policies or ACLs that would otherwise grant public access?
  4. S3 Object Ownership and ACLs: Are Access Control Lists (ACLs) disabled (recommended)? If not, does the object ACL allow access? If another account uploaded the object, do they still own it?
  5. AWS KMS Key Policies: If the object is encrypted with Customer Managed Keys (SSE-KMS), does the principal have kms:Decrypt and kms:GenerateDataKey permissions on the KMS key?
  6. VPC Endpoint Policies: If accessing S3 from a VPC via a Gateway or Interface Endpoint, does the endpoint policy restrict access to specific buckets or principals?
  7. Service Control Policies (SCPs): If your account is part of AWS Organizations, is an SCP blocking the action at the account level?

Step 1: Diagnose the Exact Cause

Before blindly changing policies, use the AWS CLI to gather information. The aws sts get-caller-identity command is your best friend to confirm who is actually making the request. Often, access denied errors occur because a script is using a default instance profile or unexpected environment variables instead of the intended credentials.

Furthermore, you can use the IAM Policy Simulator, but inspecting the CloudTrail logs is the most definitive way to see why a request failed. In CloudTrail, look for the ErrorCode: AccessDenied within the S3 data events. The ErrorMessage field sometimes provides a hint, though S3 intentionally keeps error messages vague for security reasons.

If you are experiencing what seems like aws s3 timeout or aws s3 rate limit errors, be aware that while S3 scales massively, it can throttle requests (returning 503 Slow Down) if you exceed 3,500 PUT/COPY/POST/DELETE or 5,500 GET/HEAD requests per second per prefix. While not a 403, aggressive throttling or VPC misconfigurations can sometimes manifest as timeouts that developers confuse with permission issues.

Step 2: Fix Common Scenarios

Scenario A: Missing IAM or Bucket Policy Permissions

Symptom: Your application throws software.amazon.awssdk.services.s3.model.S3Exception: Access Denied (Service: S3, Status Code: 403). Solution: Ensure the IAM role has the exact actions required. For listing a bucket, you need s3:ListBucket on the bucket ARN (arn:aws:s3:::my-bucket). For reading/writing, you need s3:GetObject or s3:PutObject on the object ARN (arn:aws:s3:::my-bucket/*).

Scenario B: KMS Encryption Permission Mismatch

Symptom: You can list objects but get a 403 when trying to download (GET) a specific object. The bucket uses SSE-KMS. Solution: The IAM role needs S3 read permissions and KMS decrypt permissions. Additionally, ensure the KMS Key Policy allows the IAM role to use the key.

Scenario C: Block Public Access is Enabled

Symptom: You attached a bucket policy allowing * (public read) but still get Access Denied when accessing the object via its HTTPS URL. Solution: Check the 'Block Public Access' tab in the S3 console. If 'Block all public access' is turned on, S3 will completely ignore any bucket policy or ACL that attempts to grant public access. You must disable the relevant BPA settings to make the bucket public. (Note: Only do this for public asset buckets like website hosting).

Scenario D: Cross-Account Object Ownership Issues

Symptom: Account A uploads an object to Account B's bucket. Account B's users cannot read the object, even as the bucket owner. Solution: By default, objects are owned by the AWS account that uploaded them. To fix this, Account B should enable 'S3 Object Ownership: Bucket owner enforced' on the bucket. This disables ACLs entirely and automatically grants the bucket owner full control over all objects, regardless of who uploaded them.

Scenario E: Unintended Explicit Deny in Bucket Policy

Symptom: Even the root user or AdministratorAccess roles get Access Denied. Solution: There is an explicit Deny in the bucket policy, VPC endpoint policy, or an SCP. A common mistake is a bucket policy that denies s3:* if aws:SecureTransport is false (forcing HTTPS), but a local script is hitting S3 over HTTP. Another common issue is restricting access to a specific aws:sourceIp and testing from an IP not in the allowlist.

Step 3: Handling Rate Limits and Throttling

If your 'access denied' is actually an aws s3 throttling issue (HTTP 503 Slow Down) or causes an aws s3 timeout, you need to optimize your request patterns. S3 automatically scales to support very high request rates, but if your application creates a sudden spike of thousands of requests per second against a single prefix, S3 needs time to partition the bucket. To resolve this:

  1. Introduce Randomness: Add random prefixes (hashes) to your object keys to distribute the load evenly across S3 partitions.
  2. Implement Exponential Backoff: Ensure your SDK clients are configured with automatic retries and exponential backoff to handle transient 503 errors gracefully.

Frequently Asked Questions

bash
# 1. Verify who you are authenticated as before testing S3 access
aws sts get-caller-identity

# 2. Test fetching an object (will return 403 if permission denied)
aws s3api get-object --bucket my-troublesome-bucket --key my-object.txt /tmp/my-object.txt

# 3. Check bucket policy for explicit Deny statements
aws s3api get-bucket-policy --bucket my-troublesome-bucket --query Policy --output text | jq .

# 4. Check if Block Public Access is enabled globally or on the bucket
aws s3api get-public-access-block --bucket my-troublesome-bucket

# 5. Check KMS key policy if object is encrypted
aws kms get-key-policy --key-id alias/my-s3-key --policy-name default --output text | jq .

# 6. Test list bucket permissions (often requires different IAM statement than GetObject)
aws s3 ls s3://my-troublesome-bucket
E

Error Medic Editorial

Our team of senior DevOps and SRE professionals distills years of cloud infrastructure experience into actionable troubleshooting guides. We focus on diagnosing and resolving complex AWS, Kubernetes, and Linux systems issues to minimize downtime.

Sources

Related Guides