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 sync