AWS basics · Lesson 5 · 40 min

S3: storage and a static website

S3 (Simple Storage Service) stores files, which S3 calls objects, in buckets. There's no disk to fill, no server to patch, and it quietly keeps several copies in different data centres. Backups, logs, photos, whole websites: an enormous part of the internet lives in S3. It's also where many famous data leaks came from, so this lesson spends as much time on who can read it as on putting things in.

You will learn

  • Buckets, objects and keys, and why bucket names are global
  • aws s3: mb, cp, ls, sync, rm, rb
  • Block Public Access, bucket policies, and hosting a static website
  • Presigned URLs for sharing one private file for a short time
  • Versioning, storage classes and lifecycle rules, briefly

Buckets, objects, keys

A bucket is a container in one region. Its name is global: unique across every AWS account in the world, so test and my-website were taken long ago. Names are lowercase letters, numbers, dots and dashes. Put something unique in them, like your account number or a random number.

An object is a file plus some metadata, stored under a key like photos/2026/cat.jpg. There are no real folders: the slashes are just part of the name, and tools show them as folders to be friendly.

aws s3 mb s3://cht-site-12345                     # make bucket
aws s3 cp notes.txt s3://cht-site-12345/          # upload
aws s3 cp s3://cht-site-12345/notes.txt copy.txt  # download
aws s3 cp s3://cht-site-12345/notes.txt -         # print it
aws s3 ls s3://cht-site-12345 --recursive --human-readable --summarize
aws s3 sync website/ s3://cht-site-12345          # upload what changed, like rsync
aws s3 sync website/ s3://cht-site-12345 --delete # …and delete what you removed locally
aws s3 rm s3://cht-site-12345/notes.txt
aws s3 rb s3://cht-site-12345 --force             # empty it and remove the bucket

aws s3 is the friendly, file-like layer. Underneath is aws s3api, with one command per API call (put-bucket-policy, get-object…). You'll use both.

Private by default

A new bucket is private: only your account can read it. On top of that, Block Public Access (BPA) is switched on for every new bucket. It's four safety switches that stop anyone from making the bucket public by accident:

aws s3api get-public-access-block --bucket cht-site-12345
{
    "PublicAccessBlockConfiguration": {
        "BlockPublicAcls": true,
        "IgnorePublicAcls": true,
        "BlockPublicPolicy": true,
        "RestrictPublicBuckets": true
    }
}

To make something public on purpose you need two steps: turn off the policy switches for that bucket, then attach a bucket policy that allows everyone ("Principal": "*") to read objects:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PublicReadForTheWebsite",
    "Effect": "Allow",
    "Principal": "*",
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::cht-site-12345/*"
  }]
}

It's the same policy language as IAM, with one extra field: Principal, meaning who the statement is about. IAM policies are attached to a person. Bucket policies are attached to the bucket, so they have to say who.

Only make public what is meant to be public

Leaked customer data, medical records and passwords have all been found in public buckets someone "just opened for a minute". Use a separate bucket for public website files, and keep everything else private. When you need to share one private file, use a presigned URL.

A website with no server

aws s3 website s3://cht-site-12345 --index-document index.html --error-document error.html
curl http://cht-site-12345.s3-website-us-east-1.amazonaws.com

S3 serves the files straight to browsers: index.html for folders, error.html for anything missing. Website endpoints are HTTP only. For HTTPS and your own domain, real sites put CloudFront (AWS's CDN) in front, and keep the bucket private.

Presigned URLs: a key for one file, for a while

aws s3 presign s3://cht-private-12345/report.pdf --expires-in 300
https://cht-private-12345.s3.us-east-1.amazonaws.com/report.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=…&X-Amz-Expires=300&X-Amz-Signature=…

The link carries a signature made with your keys. Anyone with the link can download that one file until it expires, five minutes here, and the bucket stays private. Presigning happens on your computer, with no API call at all.

Versions, classes and lifecycle

S3 costs about 2.3 cents per GB per month in us-east-1, plus a little per request and for data downloaded out to the internet.

Practice: publish a website, share a secret 🪣

There's a small website in ~/website and a policy template in ~/bucket-policy.json. Put the site on S3, make it public the careful way, then share a private file with a presigned link.

Quick check

1. aws s3 mb s3://photos fails with BucketAlreadyExists. Why?

2. You attached a public-read bucket policy, but put-bucket-policy said AccessDenied, even though you're an admin. What's blocking it?

3. How do you let a friend download one private file tomorrow, without making anything public?

Finished the missions and the quiz? Mark it done to track your progress.