---
cuppy_hub: aws
cuppy_path: guides/s3/create-secure-bucket.md
cuppy_section: docs
cuppy_doc: EFNN-BAKJ
cuppy_doc_uuid: 5e6ba2c9-6ac4-4d26-943d-eceb9d537248
cuppy_permalink: https://cuppy.io/aws/d/EFNN-BAKJ
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/guides/s3/create-secure-bucket
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 31167c0db0793f9071210e0810f1df9ab986df2bb335a8e6d2f5fdac52dd01e6
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Create a secure bucket

**A new bucket is already private and encrypted:** ACLs disabled (Bucket owner enforced), all four Block Public Access settings on, SSE-S3 encryption, and SSE-C blocked. Your job is to pick a name and Region you will not regret, confirm those defaults, and add the two things you must decide yourself: the encryption key and an HTTPS-only policy.

## 1. Name it

Rules: 3–63 lowercase letters, digits, hyphens (avoid dots), start and end alphanumeric. The name and Region are permanent, and once deleted the name can be taken by anyone else.

To make the name yours forever, use the **account regional namespace**: end the name with `-<account-id>-<region>-an` and pass `--bucket-namespace account-regional`.

## 2. Create it in the right Region

```
aws s3api create-bucket --bucket <name> --region <region> \
  --create-bucket-configuration LocationConstraint=<region>
```

`BucketAlreadyExists` (409) means another account has the name. `BucketAlreadyOwnedByYou` (409) means you already have it (treat as success in automation). What this changes: creates an empty bucket; there is no charge until you store objects or make requests (UNVERIFIED in the collected sources).

## 3. Confirm the secure defaults

```
aws s3api get-bucket-ownership-controls --bucket <name>   # expect BucketOwnerEnforced
aws s3api get-public-access-block --bucket <name>         # expect all four true (GetPublicAccessBlock)
aws s3api get-bucket-encryption --bucket <name>           # expect SSE-S3 (AES256), SSE-C blocked
```

Keep all four Block Public Access settings on unless the bucket must serve public content, and prefer setting them at the **account** level too: a bucket-level `BlockPublicPolicy` can be switched off by anyone allowed to edit the bucket's policy. If your organization uses organization-level Block Public Access, it overrides account settings anyway.

## 4. Decide on encryption

- **SSE-S3 (default):** no key management, no KMS permissions needed. Fine unless you need key-level access control or audit.
- **SSE-KMS with a customer managed key:** every reader needs `kms:Decrypt` and every writer `kms:GenerateDataKey` on the key, which gives you a second, auditable gate. Turn on a **Bucket Key** at the same time to cut KMS calls:

```
aws s3api put-bucket-encryption --bucket <name> --server-side-encryption-configuration '{
  "Rules": [{
    "ApplyServerSideEncryptionByDefault": { "SSEAlgorithm": "aws:kms", "KMSMasterKeyID": "<key ARN>" },
    "BucketKeyEnabled": true
  }]
}'
```

Use a customer managed key, not `aws/s3`, if any other account will read the data. Check whether this call must also carry the bucket's SSE-C blocking setting, so that it is not reset (UNVERIFIED how `BlockedEncryptionTypes` behaves when omitted). Full detail: `reference/s3/encryption.md`.

## 5. Require HTTPS

Attach a bucket policy with an explicit Deny for any request where `aws:SecureTransport` is `false`, covering both the bucket ARN and `<bucket ARN>/*`, and optionally a Deny where `s3:TlsVersion` is below `1.2`:

```json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "DenyInsecureTransport",
    "Effect": "Deny",
    "Principal": "*",
    "Action": "s3:*",
    "Resource": ["arn:aws:s3:::<name>", "arn:aws:s3:::<name>/*"],
    "Condition": { "Bool": { "aws:SecureTransport": "false" } }
  }]
}
```

```
aws s3api put-bucket-policy --bucket <name> --policy file://policy.json
```

A Deny with `"Principal": "*"` is not a *public grant*, so Block Public Access does not reject it. Add Allow statements for other accounts or services with fixed principals or fixed condition values, so the policy stays non-public (`reference/s3/access-control.md`).

## 6. Verify

Write and read a test object with the identity your application will use (`reference/s3/put-object.md`), and confirm a plain-HTTP request is refused. If anything is denied: `guides/s3/fix-403.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 131 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/aws/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
