Bucket Policies
A bucket policy is a resource-based IAM policy attached to an S3 bucket. Only the bucket owner can set one, and it applies to the bucket and to the objects in it that the bucket owner owns.
Bucket policies are the right tool for granting access to principals outside the account — another AWS account, an AWS service, a specific VPC endpoint — and for placing explicit denies that must hold regardless of what any identity policy says. Permissions for principals inside the account normally belong in IAM identity policies instead, which scale better and keep the bucket policy short. Bucket policies have a maximum document size, so a policy that grows a statement per team is a design that will eventually hit a wall.
Adding a policy in the console
Section titled “Adding a policy in the console”In the S3 console, open the bucket, choose the Permissions tab, and edit Bucket policy. Save applies it immediately.
If the policy grants public access and Block Public Access is enabled — which it is by default — the save is rejected. That is BlockPublicPolicy doing its job, and the fix is almost always to write a policy that is not public rather than to turn the guard off.
Structure
Section titled “Structure”{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowAnotherAccountToReadAndWrite", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::ACCOUNT-ID:root" }, "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::BUCKET-NAME/*" ] } ]}Two details catch people out. Object actions such as s3:GetObject take a resource ending in /*; bucket actions such as s3:ListBucket take the bucket ARN itself, with no trailing slash — a policy needing both lists both resources. And granting arn:aws:iam::ACCOUNT-ID:root grants the account, which still has to delegate the permission to a principal via its own IAM policy before anyone can use it.
Example: access for a specific IAM principal
Section titled “Example: access for a specific IAM principal”{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowDataTeamRole", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::ACCOUNT-ID:role/data-pipeline" }, "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": ["arn:aws:s3:::BUCKET-NAME/*"] } ]}Example: require TLS
Section titled “Example: require TLS”An explicit deny that is worth having on most buckets, because it holds no matter what else grants access:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyInsecureTransport", "Effect": "Deny", "Principal": "*", "Action": "s3:*", "Resource": [ "arn:aws:s3:::BUCKET-NAME", "arn:aws:s3:::BUCKET-NAME/*" ], "Condition": { "Bool": {"aws:SecureTransport": "false"} } } ]}Example: restrict access to one VPC endpoint
Section titled “Example: restrict access to one VPC endpoint”{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyAccessOutsideVpce", "Effect": "Deny", "Principal": "*", "Action": "s3:*", "Resource": [ "arn:aws:s3:::BUCKET-NAME", "arn:aws:s3:::BUCKET-NAME/*" ], "Condition": { "StringNotEquals": {"aws:SourceVpce": "vpce-EXAMPLE"} } } ]}Public access
Section titled “Public access”Making a bucket world-readable with "Principal": "*" and s3:GetObject was once a routine first example. It is not one now.
Since April 2023 every new bucket has Block Public Access enabled, and BlockPublicPolicy causes PutBucketPolicy to reject any policy S3 evaluates as public — which includes "Principal": "*" without a limiting condition, and conditions on very broad IP ranges. Turning the guard off to publish a bucket is nearly always the wrong move: the supported pattern for serving objects publicly is a private bucket behind a CloudFront distribution with an origin access control, which also gives TLS on a custom domain, caching and logging.
Genuine public-bucket cases exist — an open dataset, a bucket serving a static site directly — and they require Block Public Access to be relaxed deliberately, at bucket level, with the reason recorded. IAM Access Analyzer for S3 reports which buckets are public so the list can be reviewed rather than discovered.
How a bucket policy is evaluated
Section titled “How a bucket policy is evaluated”Bucket policies do not simply override IAM policies, and “the most restrictive wins” is not the rule either. What actually happens:
- An explicit deny anywhere — bucket policy, identity policy, VPC endpoint policy, service control policy, resource control policy — denies the request outright.
- Otherwise, for a principal in the same account as the bucket, an allow from either the identity policy or the bucket policy is sufficient.
- For a principal in a different account, permission is needed from both the principal’s own account and the bucket owner. Either one missing denies the request.
- Block Public Access is evaluated on top and can reject a request that the policies would have allowed.
The managing permissions page works through the evaluation order in more detail.
Practical notes
Section titled “Practical notes”- ACLs are disabled on new buckets and are not part of this picture. Access is decided by policy.
- A bucket policy cannot stop an S3 Lifecycle rule. Even a policy denying every action to every principal leaves lifecycle transitions and expirations running.
- Grant the narrowest set of actions and resources that works, and review policies periodically — most over-permissive buckets got that way one urgent exception at a time.
Reference: Bucket policies for Amazon S3.