This guide explains the static website infrastructure for putplace.org.
┌──────────────┐
│ User │
└──────┬───────┘
│ HTTPS
▼
┌──────────────────────┐
│ Route 53 DNS │
│ putplace.org │
│ www.putplace.org │
└──────┬───────────────┘
│ Alias A record
▼
┌──────────────────────┐
│ CloudFront CDN │
│ SSL/TLS Certificate │
│ Edge Caching │
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ S3 Bucket │
│ putplace.org │
│ Static Hosting │
└──────────────────────┘
- Bucket name:
putplace.org - Purpose: Static file storage
- Configuration:
- Website hosting enabled
- Public read access
- Index document:
index.html - Error document:
error.html
- Distribution ID:
YOUR_DISTRIBUTION_ID - Domain:
YOUR_CLOUDFRONT_DOMAIN.cloudfront.net - Purpose: CDN with SSL/TLS
- Configuration:
- HTTPS only (redirect HTTP to HTTPS)
- Gzip compression enabled
- Cache TTL: 24 hours (86400s)
- Custom domain aliases:
putplace.org,www.putplace.org
- Service: AWS Certificate Manager (ACM)
- ARN:
arn:aws:acm:us-east-1:YOUR_AWS_ACCOUNT_ID:certificate/YOUR_CERTIFICATE_ID - Domains:
putplace.org,www.putplace.org - Validation: DNS (automatic via Route 53)
- Region: us-east-1 (required for CloudFront)
- Hosted Zone:
YOUR_HOSTED_ZONE_ID(putplace.org) - Records:
putplace.org→ Alias A record to CloudFrontwww.putplace.org→ Alias A record to CloudFront- Certificate validation CNAME records
# 1. Create S3 bucket, certificate, and DNS records
invoke setup-static-website --domain=putplace.org
# 2. Wait for certificate validation (~5-10 minutes)
# The task automatically creates Route 53 validation records
# 3. Create CloudFront distribution
invoke create-cloudfront-distribution --domain=putplace.org
# 4. Deploy website content
invoke deploy-website --source-dir=websiteCreate S3 Bucket:
aws s3api create-bucket --bucket putplace.org --region us-east-1
# Configure website hosting
aws s3api put-bucket-website \
--bucket putplace.org \
--website-configuration file:///tmp/website-config.json
# Set public read policy
aws s3api put-bucket-policy \
--bucket putplace.org \
--policy file:///tmp/bucket-policy.jsonRequest SSL Certificate:
aws acm request-certificate \
--domain-name putplace.org \
--subject-alternative-names www.putplace.org \
--validation-method DNS \
--region us-east-1Create CloudFront Distribution:
aws cloudfront create-distribution \
--distribution-config file:///tmp/cloudfront-config.jsonThe website deploys automatically when you push changes to the main branch:
# Make changes to website content
cd website
vim index.html
# Commit and push
git add .
git commit -m "Update homepage"
git push
# GitHub Actions automatically:
# 1. Syncs files to S3
# 2. Invalidates CloudFront cache
# 3. Website updates within 1-2 minutes# Deploy website content
invoke deploy-website --source-dir=website
# This will:
# - Sync all files to S3
# - Set cache-control headers (5 minutes)
# - Invalidate CloudFront cache# Deploy Sphinx documentation
invoke deploy-website --source-dir=docs/_build/htmlThe workflow (.github/workflows/deploy-website.yml) triggers on:
- Push to
mainbranch with changes towebsite/directory - Manual workflow dispatch
Setup Requirements:
- AWS IAM OIDC Provider (if not already configured):
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1- IAM Role for GitHub Actions:
# Create role with trust policy for GitHub Actions
aws iam create-role \
--role-name GitHubActionsDeployWebsite \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR_AWS_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:jdrumgoole/putplace:*"
}
}
}]
}'
# Attach policies for S3 and CloudFront access
aws iam attach-role-policy \
--role-name GitHubActionsDeployWebsite \
--policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess
aws iam attach-role-policy \
--role-name GitHubActionsDeployWebsite \
--policy-arn arn:aws:iam::aws:policy/CloudFrontFullAccess- Add GitHub Secret:
# Add AWS_DEPLOY_ROLE_ARN to GitHub repository secrets
# Value: arn:aws:iam::YOUR_AWS_ACCOUNT_ID:role/GitHubActionsDeployWebsitewebsite/
├── index.html # Main landing page
├── error.html # 404 error page
└── README.md # Documentation
# Create new page
cat > website/about.html << EOF
<!DOCTYPE html>
<html>
<head><title>About PutPlace</title></head>
<body>
<h1>About PutPlace</h1>
<p>Information about the service...</p>
</body>
</html>
EOF
# Deploy
git add website/about.html
git commit -m "Add about page"
git push # Automatically deploys
# Access at: https://putplace.org/about.html# Test website locally before deploying
cd website
python -m http.server 8080
# Visit http://localhost:8080# View distribution status
aws cloudfront get-distribution --id YOUR_DISTRIBUTION_ID
# Check if deployed
aws cloudfront get-distribution \
--id YOUR_DISTRIBUTION_ID \
--query 'Distribution.Status' \
--output text
# Output: Deployed (ready) or InProgress (deploying)CloudFront and S3 access logs can be enabled if needed:
# Enable S3 logging
aws s3api put-bucket-logging \
--bucket putplace.org \
--bucket-logging-status file:///tmp/logging-config.json# Invalidate specific files
aws cloudfront create-invalidation \
--distribution-id YOUR_DISTRIBUTION_ID \
--paths '/index.html' '/about.html'
# Invalidate everything
aws cloudfront create-invalidation \
--distribution-id YOUR_DISTRIBUTION_ID \
--paths '/*'# CloudFront metrics in CloudWatch
aws cloudwatch get-metric-statistics \
--namespace AWS/CloudFront \
--metric-name Requests \
--dimensions Name=DistributionId,Value=YOUR_DISTRIBUTION_ID \
--start-time 2024-01-01T00:00:00Z \
--end-time 2024-01-02T00:00:00Z \
--period 3600 \
--statistics Sum# View all DNS records for putplace.org
aws route53 list-resource-record-sets \
--hosted-zone-id YOUR_HOSTED_ZONE_ID \
--query "ResourceRecordSets[?contains(Name, 'putplace.org')]"Key Records:
putplace.org(A) → CloudFront aliaswww.putplace.org(A) → CloudFront aliasapp.putplace.org(CNAME) → App Runner API- Certificate validation CNAMEs
# Test CNAME resolution
dig putplace.org A +short
# Should return CloudFront IPs
dig www.putplace.org A +short
# Should return CloudFront IPs
# Test from different DNS servers
dig @8.8.8.8 putplace.org A +short
dig @1.1.1.1 putplace.org A +shortMonthly Cost Estimate:
- S3 Storage: ~$0.023/GB = ~$0.10/month (5 GB)
- S3 Requests: $0.0004 per 1000 GET = ~$0.40/month (1M requests)
- CloudFront: First 1 TB free, then $0.085/GB
- Route 53: $0.50/month (hosted zone)
- ACM Certificate: FREE
Total: ~$1-5/month for typical usage
Check CloudFront status:
aws cloudfront get-distribution --id YOUR_DISTRIBUTION_ID- Status should be "Deployed"
- Initial deployment takes 15-20 minutes
Check DNS:
dig putplace.org A +short- Should return CloudFront IP addresses
- DNS propagation can take 5-10 minutes
Check S3 bucket:
aws s3 ls s3://putplace.org/- Should show index.html and other files
# Check certificate status
aws acm describe-certificate \
--certificate-arn arn:aws:acm:us-east-1:YOUR_AWS_ACCOUNT_ID:certificate/YOUR_CERTIFICATE_ID \
--region us-east-1
# Should show Status: ISSUED
# Both domains should show ValidationStatus: SUCCESS# Ensure error.html exists in S3
aws s3 ls s3://putplace.org/error.html
# Upload if missing
aws s3 cp website/error.html s3://putplace.org/# Create manual invalidation
aws cloudfront create-invalidation \
--distribution-id YOUR_DISTRIBUTION_ID \
--paths '/*'
# Check invalidation status
aws cloudfront list-invalidations \
--distribution-id YOUR_DISTRIBUTION_ID- S3 Bucket Policy: Only allow CloudFront access (currently public read)
- HTTPS Only: CloudFront enforces HTTPS
- WAF: Can add AWS WAF for additional protection
- Origin Access Identity: Configure CloudFront OAI for S3
Restrict S3 to CloudFront only:
# Update bucket policy to only allow CloudFront
# This prevents direct S3 website URL accessTo remove the static website infrastructure:
# 1. Delete CloudFront distribution
aws cloudfront delete-distribution \
--id YOUR_DISTRIBUTION_ID \
--if-match <ETag>
# 2. Delete S3 bucket contents and bucket
aws s3 rm s3://putplace.org --recursive
aws s3api delete-bucket --bucket putplace.org
# 3. Delete ACM certificate
aws acm delete-certificate \
--certificate-arn arn:aws:acm:us-east-1:YOUR_AWS_ACCOUNT_ID:certificate/YOUR_CERTIFICATE_ID \
--region us-east-1
# 4. Remove Route 53 DNS records
# (Keep the hosted zone if using other services)- AWS CloudFront: https://docs.aws.amazon.com/cloudfront/
- AWS S3 Static Hosting: https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html
- AWS Certificate Manager: https://docs.aws.amazon.com/acm/
- GitHub Actions AWS: https://github.com/aws-actions/configure-aws-credentials
# Setup tasks
invoke setup-static-website # Complete S3 + CloudFront setup
invoke create-cloudfront-distribution # Create CloudFront distribution
# Deployment tasks
invoke deploy-website # Deploy from website/ directory
invoke deploy-website --source-dir=docs # Deploy from custom directory
# Monitoring tasks
aws cloudfront get-distribution --id YOUR_DISTRIBUTION_ID # Check status
aws s3 ls s3://putplace.org/ # List files