Cloud Platforms No. 172

AWS CloudFront skill for Claude Code

Private origins, small cache keys, high hit ratios. Claude puts S3 behind Origin Access Control with the bucket fully private, splits traffic into per-path behaviors, and keeps cache keys limited to what changes the response while forwarding the rest through origin request policies. Assets get long immutable TTLs, HTML stays fresh, edge logic starts with CloudFront Functions, and every change is verified with x-cache headers and hit-ratio metrics.

  • nameaws-cloudfront
  • size236 lines · ≈2,800 tokens
  • files1
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Configures Amazon CloudFront distributions - origins for S3 with Origin Access Control, ALB, API Gateway and VPC origins, cache behaviors by path, managed and custom cache policies that keep the cache key small, origin request and response headers policies, TTLs versus origin Cache-Control, CloudFront Functions versus Lambda@Edge, SPA routing, signed URLs and cookies, ACM certificates in us-east-1, invalidations versus versioned assets, logging, and cost controls. Use when putting a CDN in front of S3 or an API, when CloudFront returns 403 or stale content, when the cache hit ratio is low, or when choosing edge compute.

Try asking

  • Serve our React SPA from a private S3 bucket through CloudFront with proper routing
  • CloudFront returns 403 AccessDenied for our site. Find out why
  • Our CloudFront cache hit ratio is 40%. Fix the cache policies

Use it when

  • Putting CloudFront in front of S3, an ALB, API Gateway or a Lambda URL
  • Getting 403, 502, 504 or stale content from a distribution
  • Designing cache and origin request policies per path
  • Choosing between CloudFront Functions and Lambda@Edge

Skip it when

  • You need bucket policies, presigned uploads or lifecycle rules — use s3-object-storage
  • You are designing Cache-Control headers independent of a CDN — use http-caching

How to check the result

Direct S3 requests return 403, the second request for an asset shows x-cache Hit from cloudfront, HTML changes appear within the intended TTL, and the hit ratio for static behaviors exceeds 90%.

The common mistake it prevents

Forwarding all cookies or headers in the cache key so every request becomes a cache miss, then trying to fix freshness with wildcard invalidations.

~/.claude/skills/aws-cloudfront/SKILL.md

--- name: aws-cloudfront description: Configures Amazon CloudFront distributions - origins for S3 with Origin Access Control, ALB, API Gateway and VPC origins, cache behaviors… license: MIT ---

A distribution is done when the S3 bucket is private and reachable only through CloudFront, each path pattern has a behavior with a cache key containing only what changes the response, static assets are versioned and cached for a year, dynamic paths forward exactly what the origin needs, and the cache hit ratio is measured.

Ground rules

  • S3 origins use Origin Access Control (OAC) with Block Public Access on. Never make the bucket public or use the legacy OAI for new work.
  • Cache key = only the headers, cookies and query strings that change the response. Everything else the origin needs goes in the origin request policy (forwarded, not keyed).
  • Prefer content-hashed filenames over invalidations. Invalidate HTML entry points only.
  • Certificates for CloudFront must be in ACM in us-east-1.
  • Changing behaviors, origins or the default root on a production distribution affects all traffic within minutes; show the diff and get confirmation. Disabling or deleting a distribution needs explicit confirmation.

Workflow

- [ ] 1. Inspect the distribution: origins, behaviors, policies, aliases, cert
- [ ] 2. Lock down origins (OAC for S3, custom header or VPC origin for ALB)
- [ ] 3. Define behaviors per path with cache and origin request policies
- [ ] 4. Set TTL strategy with origin Cache-Control headers
- [ ] 5. Add edge logic only where needed (Functions first)
- [ ] 6. Security: headers policy, WAF, signed URLs where required
- [ ] 7. Deploy, wait, verify with curl headers and hit-ratio metrics

1. Inspect

aws cloudfront list-distributions --query 'DistributionList.Items[].{id:Id,domain:DomainName,aliases:Aliases.Items,status:Status}'
aws cloudfront get-distribution-config --id E123ABC > dist.json   # keep ETag for updates
aws cloudfront list-cache-policies --type managed --query 'CachePolicyList.Items[].CachePolicy.{id:Id,name:CachePolicyConfig.Name}'
aws cloudfront list-origin-request-policies --type managed --query 'OriginRequestPolicyList.Items[].OriginRequestPolicy.{id:Id,name:OriginRequestPolicyConfig.Name}'

Commonly used managed policies (confirm IDs with the commands above):

PolicyUse for
Cache: CachingOptimized (658327ea-f89d-4fab-a63d-7e88639e58f6)Static assets; no cookies/headers/query in key; gzip+brotli
Cache: CachingDisabled (4135ea2d-6df8-44a3-9df3-4b5a84be39ad)APIs and personalized pages
Origin request: AllViewerExceptHostHeaderAPI Gateway/Lambda URL origins that need headers, cookies, query but reject the viewer Host
Origin request: CORS-S3OriginS3 with CORS
Response headers: SecurityHeadersPolicyBaseline HSTS, nosniff, frame options, referrer policy

2. Origins

S3 with OAC: create an OAC (signing behavior “always”, SigV4), attach it to the origin, use the bucket’s REST endpoint (bucket.s3.eu-west-1.amazonaws.com, not the website endpoint), then allow the distribution in the bucket policy:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "AllowCloudFrontOAC",
    "Effect": "Allow",
    "Principal": { "Service": "cloudfront.amazonaws.com" },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::acme-site/*",
    "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E123ABC" } }
  }]
}

SSE-KMS buckets also need the KMS key policy to allow cloudfront.amazonaws.com to kms:Decrypt with the same SourceArn condition.

OriginLock-down
S3OAC + bucket policy above; Block Public Access on
Public ALBSecret custom origin header checked by an ALB listener rule, plus SG allowing the CloudFront managed prefix list
Private ALB/NLB/EC2CloudFront VPC origins (no public exposure)
API Gateway / Lambda function URLLambda URL: OAC with AWS_IAM auth; API GW: custom header or WAF

Origin settings: HTTPS only to origin, timeouts that match the origin (default 30 s read), Origin Shield in the region nearest the origin for high fan-in or expensive origins.

3. Behaviors

Order matters: first matching path pattern wins; default * is last.

Path patternOriginCache policyOrigin request policy
/assets/* (hashed files)S3CachingOptimizednone
/api/*ALB/APICachingDisabled (or custom short TTL keyed on needed query strings)AllViewerExceptHostHeader or custom
/images/*S3 or image servicecustom: key on w, q, format query onlynone
* (HTML)S3custom: default TTL 0-60 s, honor origin headersnone

Custom cache policy example (key on two query strings, no cookies):

aws cloudfront create-cache-policy --cache-policy-config '{
  "Name": "images-wq", "MinTTL": 0, "DefaultTTL": 86400, "MaxTTL": 31536000,
  "ParametersInCacheKeyAndForwardedToOrigin": {
    "EnableAcceptEncodingGzip": true, "EnableAcceptEncodingBrotli": true,
    "HeadersConfig": { "HeaderBehavior": "none" },
    "CookiesConfig": { "CookieBehavior": "none" },
    "QueryStringsConfig": { "QueryStringBehavior": "whitelist", "QueryStrings": { "Quantity": 2, "Items": ["w", "q"] } }
  }}'

Forwarding all cookies or Authorization/User-Agent in the cache key effectively disables caching. Keying on Accept for image format negotiation is fine; on User-Agent almost never.

4. TTL strategy

CloudFront uses origin Cache-Control/Expires clamped by the policy’s Min/Max TTL; Default TTL applies only when the origin sends none.

ContentOrigin header
Hashed assetsCache-Control: public, max-age=31536000, immutable
HTML entry pointsCache-Control: public, max-age=0, s-maxage=60 (or no-cache + revalidate)
API responsesCache-Control: private, no-store unless deliberately cacheable
ErrorsError caching minimum TTL per status (default 10 s); lower for 5xx on APIs

Set headers at upload: aws s3 sync dist/ s3://acme-site/ --cache-control "public,max-age=31536000,immutable" --exclude "*.html" then upload HTML separately with the short policy. See http-caching for header design.

5. Edge logic

NeedCloudFront FunctionsLambda@Edge
URL rewrite, redirects, header add/remove, simple auth token checkYes (default)Overkill
Runs onViewer request/response onlyViewer and origin request/response
Network calls, body access, heavy librariesNoYes
Latency / costSub-millisecond, cheapestHigher, per-request + duration
Deploy regionGlobalAuthor in us-east-1, replicated
Config/data lookupCloudFront KeyValueStoreAny AWS SDK call

SPA routing with a viewer-request Function (instead of mapping 403/404 to index.html, which also hides real errors from APIs and assets):

// cloudfront-js-2.0
function handler(event) {
  var req = event.request;
  var uri = req.uri;
  if (uri.startsWith('/api/')) return req;
  if (uri.endsWith('/')) { req.uri = uri + 'index.html'; }
  else if (!uri.includes('.')) { req.uri = '/index.html'; }
  return req;
}
aws cloudfront create-function --name spa-rewrite --function-config '{"Comment":"SPA","Runtime":"cloudfront-js-2.0"}' --function-code fileb://spa.js
aws cloudfront test-function --name spa-rewrite --if-match <ETag> --event-object fileb://test-event.json
aws cloudfront publish-function --name spa-rewrite --if-match <ETag>

6. Security

  • Viewer protocol policy redirect-to-https; minimum TLS policy TLSv1.2_2021 or newer.
  • Response headers policy for HSTS, CSP (custom), and CORS when needed.
  • AWS WAF web ACL (created in us-east-1 for CloudFront) with managed rule groups and rate-based rules for login and API paths.
  • Private content: signed URLs or signed cookies with a key group (public key uploaded to CloudFront, private key in Secrets Manager); short expiry.
  • Geo restriction only for legal requirements; it is not access control.

7. Deploy and verify

aws cloudfront update-distribution --id E123ABC --if-match <ETag> --distribution-config file://dist-config.json  # confirm first
aws cloudfront wait distribution-deployed --id E123ABC
curl -sI https://www.acme.com/assets/app.3f2c1a.js | grep -iE "x-cache|age|cache-control|via"
curl -sI https://www.acme.com/assets/app.3f2c1a.js | grep -i x-cache    # second call: Hit from cloudfront
curl -sI https://acme-site.s3.eu-west-1.amazonaws.com/index.html | head -1   # expect 403 (bucket private)

Invalidations when unavoidable:

aws cloudfront create-invalidation --distribution-id E123ABC --paths "/index.html" "/"

The first 1,000 invalidation paths per month are free, then charged per path; a wildcard such as /* counts as one path but empties the whole cache and spikes origin load.

Metrics: enable additional metrics for cache hit rate and origin latency; target > 90% hit ratio for static behaviors. Standard logs (to S3, CloudWatch Logs or Firehose) for analysis; real-time logs only when needed.

Debugging

SymptomCauseFix
403 AccessDenied XML from S3OAC missing, bucket policy SourceArn wrong, KMS key policy, or object missing (S3 returns 403 for missing keys without ListBucket)Fix policy; check the key exists
403 from CloudFront on custom domainAlias not on the distribution or cert mismatchAdd alternate domain with matching ACM cert in us-east-1
502Origin TLS failure (cert name mismatch, self-signed), origin unreachableOrigin domain must match its cert; check SG and prefix list
504Origin slower than origin read timeoutFix origin or raise timeout
Stale content after deployLong TTL on unversioned filesVersion assets; short TTL on HTML; targeted invalidation
Low hit ratioCookies/headers/query strings in cache keyNarrow cache policy; move to origin request policy
API gets wrong Host / signature errorsViewer Host forwarded to API Gateway or Lambda URLAllViewerExceptHostHeader

Gotchas

  • Using the S3 website endpoint as origin forces a custom origin and prevents OAC.
  • Default root object applies only to /, not subdirectories; use a Function for /docs/ -> /docs/index.html.
  • Distribution changes take minutes to deploy globally; wait distribution-deployed before testing.
  • Lambda@Edge logs land in CloudWatch in the region of the edge location that ran it.
  • Compression only happens when the cache policy enables gzip/brotli and the object is between 1 KB and 10 MB with a compressible content type.
  • Flat-rate pricing plans (introduced late 2025) bundle CloudFront with WAF and other features per distribution; compare against pay-as-you-go for your traffic before switching.

Output

**Distribution:** E123ABC (www.acme.com, ACM us-east-1, TLSv1.2_2021)
**Origins:** S3 acme-site via OAC (bucket private, verified 403 direct); ALB via VPC origin
**Behaviors:** /assets/* CachingOptimized; /api/* CachingDisabled + AllViewerExceptHostHeader; * HTML 60 s
**Edge:** CloudFront Function spa-rewrite (viewer request)
**Security:** SecurityHeadersPolicy + CSP; WAF managed rules + rate limit on /api/login
**Verified:** x-cache Hit on assets; hit ratio 96% (static) after 24 h; HTML updates visible in 60 s

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc