---
cuppy_hub: gcp
cuppy_path: guides/storage/create-a-bucket-that-is-not-public.md
cuppy_section: docs
cuppy_doc: 98JC-98ZQ
cuppy_doc_uuid: 52d691da-3b7c-4eaf-a0f5-7ab545b6e155
cuppy_permalink: https://cuppy.io/gcp/d/98JC-98ZQ
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/gcp/docs/guides/storage/create-a-bucket-that-is-not-public
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: d57baf44b0f3c1c9f5da35055e229c75142a3308d3412e391aa1c0c6d6034209
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# A bucket that cannot accidentally become public

## Step 1 — decide the location type, because it is a create-time decision

`zone` (only with the `RAPID` class), `region`, `dual-region` or `multi-region`. Decide against the data's
residency requirement and its readers' geography, and decide **now**: the location is part of what you create,
not something you reconfigure afterwards.

## Step 2 — decide the class, knowing what it commits

`STANDARD` is the default. `NEARLINE`, `COLDLINE` and `ARCHIVE` carry minimum storage durations of **30, 90 and
365 days** and all three carry retrieval fees. Pick the class the data's *actual* access pattern justifies, and
check it against any retention or deletion rule you are also about to write
(`reference/storage/cloud-storage.md` §2).

## Step 3 — enable uniform bucket-level access **at creation**

This is the step the whole guide exists for. Uniform bucket-level access "allows you to use Identity and Access
Management (IAM) alone to manage permissions", and Google marks it **"(recommended)"** because fine-grained
access brings "an increased chance of unintentional data exposure".

Doing it at creation also sidesteps the one-way door: you have **90 days** to switch back "before uniform
bucket-level access becomes permanent", and on a brand-new bucket with no consumers that window costs nothing.
On an inherited bucket it is a 90-day clock on an unproven change
(`reference/storage/cloud-storage-access-control.md` §3).

## Step 4 — turn on public access prevention

It blocks `allUsers` and `allAuthenticatedUsers`. Note what the second one is **not**: it is not
"authenticated to my organization", it is any authenticated Google account anywhere. Most accidental public
buckets are that misreading.

## Step 5 — grant at the bucket, not at the project

Bind the predefined role the caller actually needs on the **bucket** — `roles/storage.objectViewer` to read
objects, not `roles/storage.admin` on the project. Inheritance runs downward, so a project-level grant reaches
every bucket you will ever create there (`reference/identity/iam-model.md` §2).

If a browser or a third party needs one object, that is a **signed URL**, not a grant and never a public
bucket.

## Step 6 — verify by attempting the thing you forbade

A configuration you have not tried is a belief. Make these three calls:

| Attempt | Expected |
|---|---|
| read an object as an unauthenticated caller | **401 `required`** or **403 `forbidden`** — never a 200 |
| set an object ACL | **400 `badRequest`** — "The request cannot be completed based on your current Cloud Storage settings". That 400 is uniform bucket-level access working |
| read an object as the intended workload identity | **200** |

If the first returns 200 the bucket is public; stop and fix it before anything else is written to it. If the
second returns 200, uniform bucket-level access is **not** on.

## For a migration

Every bucket created this way removes a class of finding from the security review on the far side, and the two
settings in steps 3 and 4 have no per-object equivalent to audit afterwards. That is the cheapest hour in a
storage migration. The counterpart settings on AWS (S3 Block Public Access, bucket owner enforced) and Azure
(disallow public blob access) are in `guides/parity/storage.md`.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 91 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/gcp/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
