AWS S3 & CloudFront: Fix 403 Forbidden Errors Caused by Origin Access Control (OAC) Misconfiguration
Quick Fix Summary
TL;DRVerify and update the CloudFront distribution's Origin Access Control (OAC) policy on the S3 bucket.
A 403 Forbidden error occurs when CloudFront cannot access the S3 origin bucket due to a misconfigured or missing Origin Access Control (OAC) policy, which replaces the legacy Origin Access Identity (OAI).
Diagnosis & Causes
Recovery Steps
Step 1: Verify the CloudFront Distribution's OAC Configuration
Identify the OAC ID attached to your CloudFront distribution's origin.
aws cloudfront get-distribution-config --id YOUR_DISTRIBUTION_ID --query 'DistributionConfig.Origins.Items[?Id=="YOUR_ORIGIN_ID"].S3OriginConfig.OriginAccessIdentity' --output text Step 2: Retrieve the OAC's S3 Canonical User ID
Get the unique canonical user ID that must be referenced in the S3 bucket policy.
aws cloudfront get-origin-access-control --id YOUR_OAC_ID --query 'OriginAccessControl.OriginAccessControlConfig.SigningProtocols' --output text
# Note the 'S3CanonicalUserId' from the full output:
aws cloudfront get-origin-access-control --id YOUR_OAC_ID Step 3: Inspect and Correct the S3 Bucket Policy
Check the S3 bucket policy and ensure it grants access to the OAC's canonical user. Replace the policy if incorrect.
aws s3api get-bucket-policy --bucket YOUR_BUCKET_NAME --query Policy --output text
# Apply a corrected policy. Replace YOUR_BUCKET_NAME and OAC_CANONICAL_USER_ID.
aws s3api put-bucket-policy --bucket YOUR_BUCKET_NAME --policy '{"Version":"2012-10-17","Statement":[{"Sid":"AllowCloudFrontOAC","Effect":"Allow","Principal":{"CanonicalUser":"OAC_CANONICAL_USER_ID"},"Action":"s3:GetObject","Resource":"arn:aws:s3:::YOUR_BUCKET_NAME/*"}]}' Step 4: Validate OAC is Enabled on the Origin
Confirm the CloudFront origin is configured to use OAC and not 'None'.
aws cloudfront get-distribution-config --id YOUR_DISTRIBUTION_ID --query 'DistributionConfig.Origins.Items[?Id=="YOUR_ORIGIN_ID"].S3OriginConfig.OriginAccessIdentity' --output text
# Output should be 'origin-access-control/cloudfront/YOUR_OAC_ID', not 'None'. Step 5: Create and Attach a New OAC (If Missing)
If no OAC exists, create one and update the CloudFront distribution.
# 1. Create OAC
OAC_ARN=$(aws cloudfront create-origin-access-control --origin-access-control-config '{"Name":"MyOAC","Description":"","SigningProtocol":"sigv4","SigningBehavior":"always","OriginAccessControlOriginType":"s3"}' --query 'OriginAccessControl.Id' --output text)
# 2. Update CloudFront distribution config to use this OAC_ID. This requires a full config JSON update via update-distribution. Step 6: Invalidate CloudFront Cache
After fixing the policy, invalidate the cache to ensure corrected objects are served.
aws cloudfront create-invalidation --distribution-id YOUR_DISTRIBUTION_ID --paths "/*" Architect's Pro Tip
"This often happens during migration from OAI to OAC. The S3 bucket policy Principal for OAC must be the 'CanonicalUser' ID from the OAC, not an ARN. Double-check this exact value."
Frequently Asked Questions
What's the difference between OAI and OAC?
OAI (legacy) uses an IAM-like identity. OAC (newer) uses a canonical user ID and supports advanced features like S3 SSE-KMS. OAC is the recommended method.
I updated the bucket policy but still get 403. What's wrong?
1. The CloudFront distribution may still be using the old OAI. Verify the origin config. 2. There may be an explicit 'Deny' in the bucket policy overriding your 'Allow'. 3. The OAC might be disabled ('None') on the origin. Check all three.