Create a bucket that is private, encrypted and HTTPS-only by default

Choose a durable name (or the account regional namespace), create the bucket in the right Region, confirm the secure defaults you already get (ACLs off, Block Public Access on, SSE-S3), then add what you decide on purpose — SSE-KMS with a Bucket Key, and an HTTPS/TLS policy.

markdownguidev4EFNN-BAKJstage: buildtrust: officialservice: s3interface: cliinterface: iaccreate buckets3api create-bucketsecure bucketBlock Public AccessObject Ownershipdefault encryptionSSE-KMSBucket Keyaws:SecureTransportaccount regional namespace

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

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:

{
  "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.

Read this document as markdown — the same bytes an agent receives.