install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/TerminalSkills/skills/aws-cloudfront" ~/.claude/skills/comeonoliver-skillshub-aws-cloudfront && rm -rf "$T"
manifest:
skills/TerminalSkills/skills/aws-cloudfront/SKILL.mdsource content
AWS CloudFront
Amazon CloudFront is a global CDN that delivers content from 400+ edge locations. It caches static and dynamic content, terminates SSL, and integrates with S3, ALB, and API Gateway as origins.
Core Concepts
- Distribution — a CloudFront deployment with origins and behaviors
- Origin — the source of content (S3, ALB, custom HTTP server)
- Cache Behavior — rules for how requests are handled per URL pattern
- Origin Access Control (OAC) — restricts S3 access to CloudFront only
- Lambda@Edge — run code at edge locations on request/response
- Invalidation — remove cached content before TTL expires
Creating a Distribution
// dist-config.json — CloudFront distribution for S3 static site + ALB API { "CallerReference": "my-app-2024", "Comment": "My App CDN", "DefaultCacheBehavior": { "TargetOriginId": "s3-static", "ViewerProtocolPolicy": "redirect-to-https", "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", "Compress": true, "AllowedMethods": ["GET", "HEAD"], "CachedMethods": ["GET", "HEAD"] }, "Origins": { "Quantity": 2, "Items": [ { "Id": "s3-static", "DomainName": "my-app-assets.s3.us-east-1.amazonaws.com", "OriginAccessControlId": "EABCDEF123456", "S3OriginConfig": {"OriginAccessIdentity": ""} }, { "Id": "alb-api", "DomainName": "app-alb-123456.us-east-1.elb.amazonaws.com", "CustomOriginConfig": { "HTTPPort": 80, "HTTPSPort": 443, "OriginProtocolPolicy": "https-only" } } ] }, "CacheBehaviors": { "Quantity": 1, "Items": [{ "PathPattern": "/api/*", "TargetOriginId": "alb-api", "ViewerProtocolPolicy": "https-only", "CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad", "OriginRequestPolicyId": "216adef6-5c7f-47e4-b989-5492eafa07d3", "AllowedMethods": ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"], "CachedMethods": ["GET", "HEAD"] }] }, "DefaultRootObject": "index.html", "Enabled": true, "PriceClass": "PriceClass_100", "ViewerCertificate": { "ACMCertificateArn": "arn:aws:acm:us-east-1:123456789:certificate/abc-123", "SSLSupportMethod": "sni-only", "MinimumProtocolVersion": "TLSv1.2_2021" }, "Aliases": {"Quantity": 1, "Items": ["app.example.com"]} }
# Create distribution aws cloudfront create-distribution --distribution-config file://dist-config.json
Origin Access Control
# Create OAC to restrict S3 access to CloudFront aws cloudfront create-origin-access-control \ --origin-access-control-config '{ "Name": "my-app-oac", "OriginAccessControlOriginType": "s3", "SigningBehavior": "always", "SigningProtocol": "sigv4" }'
// S3 bucket policy allowing only CloudFront { "Version": "2012-10-17", "Statement": [{ "Sid": "AllowCloudFront", "Effect": "Allow", "Principal": {"Service": "cloudfront.amazonaws.com"}, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::my-app-assets/*", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::123456789:distribution/E1234567890" } } }] }
Cache Invalidation
# Invalidate specific paths aws cloudfront create-invalidation \ --distribution-id E1234567890 \ --paths '/index.html' '/static/app.js' '/api/config'
# Invalidate everything (use sparingly — costs per path) aws cloudfront create-invalidation \ --distribution-id E1234567890 \ --paths '/*'
# Check invalidation status aws cloudfront list-invalidations --distribution-id E1234567890
Lambda@Edge
# lambda-edge/viewer-request.py — add security headers and redirect def handler(event, context): request = event['Records'][0]['cf']['request'] uri = request['uri'] # SPA routing: serve index.html for non-file paths if '.' not in uri.split('/')[-1]: request['uri'] = '/index.html' return request
# lambda-edge/origin-response.py — add security headers def handler(event, context): response = event['Records'][0]['cf']['response'] headers = response['headers'] headers['strict-transport-security'] = [{ 'key': 'Strict-Transport-Security', 'value': 'max-age=63072000; includeSubDomains; preload' }] headers['x-content-type-options'] = [{ 'key': 'X-Content-Type-Options', 'value': 'nosniff' }] headers['x-frame-options'] = [{ 'key': 'X-Frame-Options', 'value': 'DENY' }] return response
Custom Error Pages
# Configure custom error responses (SPA fallback) aws cloudfront update-distribution \ --id E1234567890 \ --if-match ETAG123 \ --distribution-config '...' # Include CustomErrorResponses:
// Custom error responses for SPA routing { "CustomErrorResponses": { "Quantity": 2, "Items": [ {"ErrorCode": 403, "ResponsePagePath": "/index.html", "ResponseCode": "200", "ErrorCachingMinTTL": 10}, {"ErrorCode": 404, "ResponsePagePath": "/index.html", "ResponseCode": "200", "ErrorCachingMinTTL": 10} ] } }
Monitoring
# Get distribution details aws cloudfront get-distribution --id E1234567890 \ --query 'Distribution.[DomainName,Status,DistributionConfig.Enabled]'
# Enable real-time logs aws cloudfront create-realtime-log-config \ --name app-realtime-logs \ --sampling-rate 100 \ --fields "timestamp" "c-ip" "sc-status" "cs-uri-stem" "time-taken" \ --end-points '[{"StreamType":"Kinesis","KinesisStreamConfig":{"RoleARN":"arn:aws:iam::123456789:role/cf-realtime-logs","StreamARN":"arn:aws:kinesis:us-east-1:123456789:stream/cf-logs"}}]'
Best Practices
- Use Origin Access Control (not legacy OAI) for S3 origins
- Enable compression for text-based assets (HTML, CSS, JS, JSON)
- Use managed cache policies instead of custom forwarding settings
- Invalidate sparingly — use versioned filenames (app.v2.js) instead
- Set appropriate TTLs: long for static assets, short for dynamic content
- Use Lambda@Edge for SPA routing, A/B testing, and auth at the edge
- Enable HTTPS everywhere with ACM certificates (must be in us-east-1)