The Scenario
You’re deploying a static site via an S3 bucket fronted by CloudFront. The CI/CD pipeline pushes assets fine, but the CloudFront distribution starts returning 403 errors after a recent IAM role update. Users see a blank page, and your monitoring alerts start firing on a busy Friday afternoon.
Symptoms
AccessDenied(403) returned when accessing S3 objects via HTTP or CLI- CloudFront returns
403 ERROR The request could not be satisfied - AWS CLI command
aws s3 cp s3://my-bucket/file.txt .fails with:fatal error: An error occurred (AccessDenied) when calling the GetObject operation: Access Denied
Root Cause
The S3 bucket policy, IAM role, or bucket ACL is denying the request. This usually stems from an explicit Deny in a bucket policy, a missing s3:GetObject permission on the IAM role, or the S3 Block Public Access settings overriding a permissive policy. CloudFront adds another layer — the Origin Access Control (OAC) or Origin Access Identity (OAI) must be correctly configured and attached.
Resolution (Step-by-Step)
-
Check the effective permissions for the principal
Use the AWS Policy Simulator with the exact IAM role/ARN and the S3 action (e.g.,s3:GetObject).aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::123456789012:role/my-app-role \ --action-names s3:GetObject \ --resource-arns arn:aws:s3:::my-bucket/* -
Inspect the S3 bucket policy
Look for explicitDenystatements or missingAllowfor the principal.aws s3api get-bucket-policy --bucket my-bucket -
Verify S3 Block Public Access settings
If you need public access, ensureBlockPublicAcls,BlockPublicPolicy,IgnorePublicAcls, andRestrictPublicBucketsare all set tofalse.aws s3api get-public-access-block --bucket my-bucket -
For CloudFront: Validate the OAC/OAI configuration
Ensure the CloudFront distribution’s origin is set to use an OAC (recommended) or OAI, and the S3 bucket policy allows that identity.aws cloudfront get-distribution-config --id E1234567890ABCThen check the bucket policy includes a statement like:
{ "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::my-bucket/*", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E1234567890ABC" } } } -
Check IAM role trust policy and permissions boundary
If the principal is an IAM role, confirm the trust policy allows the service (e.g., EC2, Lambda) to assume it, and no permissions boundary restricts S3 access.
Why This Sometimes Doesn’t Work
The most common trap is fixing the bucket policy or IAM role but forgetting about the S3 Block Public Access settings at the account level. Even if the bucket-level settings are permissive, the account-level BlockPublicAcls or RestrictPublicBuckets can silently override them. Always check both the bucket and account-level configurations. Another gotcha: CloudFront OAC requires the s3:GetObject action and the s3:GetObjectVersion action if you’re using versioning — missing that second action causes a 403.
Verification
Run a HEAD request on an object to confirm access:
curl -I https://my-bucket.s3.amazonaws.com/file.txt
# Expected: HTTP/2 200
Or use the AWS CLI with the exact principal:
aws s3api head-object --bucket my-bucket --key file.txt
# Expected: no error, returns metadata
Common Follow-up Questions
Q: Why does the error only happen for some objects?
A: Check the object-level ACLs or if the bucket policy uses Principal conditions that exclude certain paths. Also verify that object keys don’t contain special characters that break the policy.
Q: Does enabling S3 Transfer Acceleration affect permissions?
A: No, Transfer Acceleration only changes the endpoint, not the access control. If you get a 403, the issue is still in your IAM/policy config.
Q: How do I debug CloudFront-to-S3 403 specifically?
A: Temporarily bypass CloudFront and access the S3 object directly. If that works, the issue is in the CloudFront origin (OAC/OAI) or the bucket policy’s CloudFront-specific statement.
Prevent This in the Future
Set up proactive monitoring on your S3 bucket policies and IAM role changes to catch permission drift before it hits users. Use a tool like Better Stack to monitor CloudFront error rates and S3 access logs, alerting you when 403s spike above a threshold.
Set up uptime monitoring and alerts with Better Stack