AWS S3 AccessDenied after bucket policy change — the fix
Changed your S3 bucket policy and now you're locked out? Here's the exact fix and why it happens.
Yeah, that sinking feeling when you update an S3 bucket policy and immediately lose access. I've been there. Don't panic — this is almost always fixable without a support ticket.
The quick fix: use the root AWS account
The root account (the email you signed up with) can always override any S3 bucket policy. Log out, log in as root, then go to the S3 console. You'll see the bucket. Edit the policy immediately.
- Sign in as root (that's the email address, not an IAM user)
- Navigate to S3 → your bucket → Permissions → Bucket Policy
- Delete the entire policy or replace it with a safe one (see below)
- Click Save — access restored instantly
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket/*"
}
]
}
Important: This policy allows public read access. Use it only to regain access, then lock it down properly. For a private bucket, set it to deny all except your specific IAM roles.
Why this happens
The culprit is almost always an explicit deny in the bucket policy that applies to the IAM user or role you're using. S3 evaluates policies like this:
- Explicit Deny wins over everything
- Then Allow
- Default Deny (implicit) if no Allow matches
So if your bucket policy says "Effect": "Deny" for "Principal": "*" (or for your specific IAM user), that deny overrides any Allow you have attached to your IAM role or user. Root account bypasses this — it's the only principal that isn't bound by bucket policies.
Common mistake: You copied a policy from a blog post that had a deny-all statement for testing, then forgot to remove it. Or you tried to restrict access to a specific IP range and accidentally blocked yourself.
Less common variations of the same issue
1. Block Public Access settings
Even if your bucket policy allows public access, S3's Block Public Access settings can override it. Go to Permissions → Block Public Access and check if any boxes are checked. If they are, uncheck them — but only if you actually want public access. For private buckets, leave them checked.
2. S3 Object Lambda policies
If you're using S3 Object Lambda, the access point policy can conflict with the bucket policy. You'll see AccessDenied even though the bucket policy looks correct. Fix: check the access point policy under S3 → Object Lambda Access Points and make sure it's not denying your principal.
3. Cross-account access with condition keys
You might have a condition like "aws:SourceIp": "10.0.0.0/8" that doesn't match your current IP. This is sneaky because the error message doesn't tell you which condition failed. Use IAM Policy Simulator to debug — it shows which condition blocked the request.
4. KMS encryption key permissions
If your bucket uses SSE-KMS (KMS-managed keys), you also need kms:Decrypt permission on the key. The error will still say AccessDenied, not something helpful like "KMS key not found". Check the key policy in KMS console — your IAM user or role must be listed as a key user.
How to prevent this in the future
- Test policies with IAM Policy Simulator before applying them. It's under IAM → Policy Simulator. Pick your user, the S3 action, and the bucket ARN. It'll show you exactly what effect the policy has.
- Use a separate IAM admin user with full S3 permissions for policy changes. Never use your daily driver account for this — if you lock out your admin account, you can still use root to fix it.
- Apply policies in stages. Start with a simple Allow, then add Deny statements one at a time. Test each change with the simulator or by making a small request like
aws s3 ls s3://your-bucket/from the CLI. - Use AWS CloudTrail to log all S3 API calls. If you lock yourself out again, CloudTrail logs show which principal made the request and what policy was evaluated. Filter by
errorCode: "AccessDenied"and look at therequestParametersfield. - Keep a backup of your working policy as a JSON file locally. If you break it, you can restore it from the backup instead of guessing.
One last thing: if you're using AWS Organizations with SCPs (Service Control Policies), those can also cause AccessDenied. SCPs act as a global deny filter at the account level. Check Organizations → Policies if the root fix doesn't work — though that's rare for a bucket policy change.
That's it. You're back in. Now go lock down that bucket properly and never touch a Deny statement without testing it first.
Was this solution helpful?