Use S3 to host static websites, store application assets, and manage file uploads with presigned URLs and bucket policies.
S3 stores objects (files) in buckets. It's the foundation of most AWS architectures: static website hosting, file uploads, build artifacts, logs, backup storage, and AI dataset storage. The pricing is minimal and it scales infinitely.
# Bucket names must be globally unique aws s3 mb s3://my-app-static-site-2026 # Enable static website hosting aws s3 website s3://my-app-static-site-2026 \ --index-document index.html \ --error-document error.html # Upload files aws s3 sync ./dist s3://my-app-static-site-2026 # Make files publicly readable aws s3api put-bucket-policy \ --bucket my-app-static-site-2026 \ --policy file://bucket-policy.json
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-app-static-site-2026/*"
}]
}
Your site will be available at http://my-app-static-site-2026.s3-website-us-east-1.amazonaws.com. In Day 5 we'll put CloudFront + HTTPS in front of it.
For user file uploads, never expose your AWS credentials to the browser. Instead, generate a presigned URL on the server — a time-limited URL that allows a specific S3 operation without needing credentials.
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3 = new S3Client({ region: 'us-east-1' });
export async function getUploadUrl(filename, contentType) {
const key = `uploads/${Date.now()}-${filename}`;
const command = new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: key,
ContentType: contentType,
});
const url = await getSignedUrl(s3, command, { expiresIn: 300 }); // 5 min
return { url, key };
}
// Express route
app.post('/api/upload-url', async (req, res) => {
const { filename, contentType } = req.body;
const { url, key } = await getUploadUrl(filename, contentType);
res.json({ url, key });
});
Bucket policies attach to the bucket and apply to anyone accessing it (including anonymous users if you use "Principal": "*"). IAM policies attach to users and roles. For public read, use a bucket policy. For your app's write access, use an IAM role.
aws s3 syncThe foundations from today carry directly into Day 3. In the next session the focus shifts to Day 3 — building directly on everything covered here.
Before moving on, verify you can answer these without looking:
Live Bootcamp
Learn this in person — 2 days, 5 cities
Thu–Fri sessions in Denver, Los Angeles, New York, Chicago, and Dallas. $1,490 per seat. June–October 2026.
Reserve Your Seat →