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.
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
- Versioning (
put-bucket-versioning … Status=Enabled) keeps every old version of every object. Overwrites and deletes become undoable. A "delete" only adds a delete marker, so versioned buckets keep costing money until you remove old versions too. - Storage classes trade price for speed:
STANDARDfor everyday files,STANDARD_IAfor files you rarely read, andGLACIER/DEEP_ARCHIVEfor archives that take hours to get back. - Lifecycle rules move or delete objects automatically, for example "logs go to Glacier after 30 days and are deleted after a year".
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?
✓ Add something unique: your account ID, a project name, a random number.
2. You attached a public-read bucket policy, but put-bucket-policy said AccessDenied, even though you're an admin. What's blocking it?
✓ It's a safety catch: public policies are refused until you deliberately switch it off for that bucket.
3. How do you let a friend download one private file tomorrow, without making anything public?
✓ Presigned URLs can last up to 7 days, and the bucket stays private.