highFoundationSymptom-first#s3#403#bucket-policy#block-public-access

S3 returns 403 — bucket policy or public access block

All client types get 403 Forbidden on a bucket

Likely source:securityconfiguration

Focused incident mode — strips away everything but this runbook.

Symptoms

  • All client types get 403 Forbidden on a bucket
  • Access worked until an account/config change
  • Website/static assets suddenly break with Access Denied

Bucket policies and Public Access Blocks are separate features that love to fight: BlockPublicPolicy makes a public bucket policy fail at PUT time, while a policy without it 403s every read. Audit both before blaming IAM.

Evidence to grab

Collect these artifacts before you change anything — they prove (or rule out) each cause.

  • Bucket policy

    aws s3api get-bucket-policy --bucket <BUCKET>
  • Public access block

    aws s3api get-public-access-block --bucket <BUCKET>

Diagnostic flow

Run the steps in order, marking each claim confirmed or refuted. The evidence you gather feeds the ranked causes below.

Guided decision tree is ready.

Steps with commands, expected output, and pass/fail branches.

Ranked root causes

Evidence checklist

  • Policy references a source/VPC or condition the caller fails
  • Recent policy push precedes the 403

Confirm before fixing

aws s3api get-bucket-policy --bucket <BUCKET> --query Policy --output text | grep -A2 -B2 '<CALLER-ARN>'
READ

Find the caller in the policy

Fix

aws s3api put-bucket-policy --bucket <BUCKET> --policy file://corrected-policy.json
CHANGE

Fix the policy (keep an explicit Deny ONLY if intentional)

Resolution runbook

Apply from top to bottom after the cause is confirmed. Review every command before running it — risk tags are shown on each.

1
aws sts get-caller-identity
READ

Know the caller

2
aws s3api get-bucket-policy --bucket <BUCKET>
READ

Audit the policy

3
aws s3api get-public-access-block --bucket <BUCKET>
READ

Audit the block

4
aws s3api put-bucket-policy --bucket <BUCKET> --policy file://corrected-policy.json
CHANGE

Apply the corrected policy

5
aws s3 ls s3://<BUCKET>/
READ

Verify the fix

Expected: A listing with no AccessDenied

Prevention

  • Apply public access block at the account level; use explicit exceptions sparingly.
  • Lint bucket policies with IAM Access Analyzer before applying.
  • Test public access with an anonymous role in CI after every policy change.

Commands marked READ CHANGE DESTRUCTIVE — never run DESTRUCTIVE commands against production data you cannot restore. Copy → verify → execute.