Blocking Public Access
Almost every famous “company leaks millions of records on an open S3 bucket” headline traces back to one mistake: a bucket that was accidentally made public. Amazon S3 (Simple Storage Service, AWS’s object storage) ships with a feature called Block Public Access (BPA) that exists to stop exactly this. It is a safety net that sits above your bucket policies and ACLs (Access Control Lists), and it can override anything that would expose your data. This page explains the four settings, how account scope and bucket scope interact, and the one rule that prevents almost all S3 leaks: leave it on.
What Block Public Access actually does
Block Public Access is not another permission rule that gets merged with your policies. It is a veto. Even if a bucket policy says "Principal": "*" with "Effect": "Allow" (meaning “anyone on the internet may read this”), BPA can simply refuse to honor it. Think of it as a master switch that says “I do not care what the policies say — do not let this become public.”
It works at two scopes:
| Scope | Set on | Effect |
|---|---|---|
| Account level | The whole AWS account (per Region) | Applies to every bucket in the account, current and future |
| Bucket level | One individual bucket | Applies only to that bucket |
The two combine with a simple OR rule: if either the account or the bucket blocks something, it is blocked. A bucket can be more restrictive than the account, but it can never be less restrictive. So turning BPA on account-wide is a powerful guardrail — no individual bucket can quietly opt out and go public.
Since April 2023, AWS enables all four BPA settings by default on every new bucket, and ACLs are disabled by default too. This is a deliberate “secure by default” stance.
The four settings explained
BPA is made of four independent toggles. They split along two lines: ACLs vs policies, and block new vs ignore/restrict existing.
| Setting | What it controls | Plain-English meaning |
|---|---|---|
BlockPublicAcls | New requests | Reject any PUT request that tries to set a public ACL |
IgnorePublicAcls | Existing state | Ignore all public ACLs already on the bucket or its objects |
BlockPublicPolicy | New requests | Reject any bucket policy that would grant public access |
RestrictPublicBuckets | Existing state | If the bucket is already public, limit access to AWS service principals and authorized users only |
The first two deal with ACLs (the legacy 2006-era per-object permission system). The last two deal with bucket policies. The “Block” settings stop you from creating public access; the “Ignore”/“Restrict” settings neutralize public access that already exists. Enabling all four gives you the strongest protection, which is why that is the recommended default.
Gotcha: The overwhelming majority of S3 data leaks come from someone turning BPA off “just to test something” and then forgetting to turn it back on. Treat disabling BPA as a high-risk change that needs review, never a casual experiment.
When to use this (and when not to)
Use it: always, on every bucket. There is no normal scenario where a private bucket benefits from BPA being off. Logs, backups, application uploads, data lakes, build artifacts — all should keep BPA fully enabled.
The one exception is a bucket whose entire purpose is to serve public content directly (a legacy static website endpoint). Even then, the modern answer is not to disable BPA. Instead, keep the bucket private and serve it through Amazon CloudFront (AWS’s content delivery network) using Origin Access Control (OAC). CloudFront fetches objects from the private bucket on the viewer’s behalf, so the world can read your content while the bucket itself stays locked down. You get HTTPS, caching, and a custom domain for free.
| Approach | Bucket public? | Recommended? |
|---|---|---|
| Make the bucket public via policy | Yes | No — last resort only |
| Keep BPA on, front with CloudFront + OAC | No | Yes — the modern standard |
There is no extra charge for OAC; you pay normal CloudFront request and data-transfer rates, which for public content are usually cheaper than serving directly from S3 anyway.
Checking and setting BPA
Via the console
- Open the S3 console and click your bucket name (for account-wide settings, choose Block Public Access settings for this account in the left sidebar instead).
- Go to the Permissions tab.
- Find Block public access (bucket settings) and choose Edit.
- Tick Block all public access (this selects all four) and choose Save changes.
- Type
confirmin the dialog and confirm.
Via the AWS CLI
Check the current state of a bucket:
aws s3api get-public-access-block --bucket devcraftly-assets
Output:
{
"PublicAccessBlockConfiguration": {
"BlockPublicAcls": true,
"IgnorePublicAcls": true,
"BlockPublicPolicy": true,
"RestrictPublicBuckets": true
}
}
Enable all four on a single bucket:
aws s3api put-public-access-block \
--bucket devcraftly-assets \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
This command prints nothing on success.
Lock it down for the entire account (using your 12-digit account ID):
aws s3control put-public-access-block \
--account-id 111122223333 \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
Tip: Set BPA at the account level once. After that, no engineer can accidentally make any bucket public, even with a wide-open bucket policy. This single setting prevents the most common cause of S3 breaches.
Enforcing it as code
If you manage buckets with CloudFormation, the BPA block is part of the bucket resource:
Resources:
AssetsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: devcraftly-assets
PublicAccessBlockConfiguration:
BlockPublicAcls: true
IgnorePublicAcls: true
BlockPublicPolicy: true
RestrictPublicBuckets: true
Defining it in your template means every deploy re-asserts the secure state, so a manual change in the console gets reverted on the next stack update.
What happens when BPA is on
If a public bucket policy is present but BPA blocks it, anonymous requests return 403 Access Denied, and the S3 console clearly labels the bucket as “Not public.” If your “public” objects unexpectedly return Access Denied, check BPA before you start debugging the policy — nine times out of ten, BPA is doing its job correctly.
Best Practices
- Enable all four BPA settings at the account level so no bucket can ever go public by accident.
- Keep BPA on for every individual bucket; never disable it “to test.”
- Serve public content through CloudFront with Origin Access Control, keeping the bucket itself private.
- Define BPA in CloudFormation or Terraform so the secure state is enforced on every deploy.
- Use IAM Access Analyzer for S3 to get alerts the moment any bucket becomes publicly accessible.
- Keep ACLs disabled (Object Ownership = Bucket owner enforced) so the ACL-related BPA settings have less to defend against.
- Treat any request to turn off BPA as a reviewed, documented change — not a quick fix.