Skip to content
AWS aws dns-cdn 6 min read

Origins, Behaviors & Caching

A CloudFront distribution is a global content delivery network (CDN — a fleet of edge servers near your users that cache copies of your content). But a CDN is only fast if it actually serves cached copies instead of phoning home to your server every time. Three knobs control this: origins (where your content really lives), cache behaviors (which origin handles which URL path), and cache policies (how CloudFront decides whether two requests are “the same”). Getting the cache key right is the single biggest lever on how effective your CDN is, so this page focuses there.

Origins

An origin is the source of truth that CloudFront pulls content from when it does not already have a cached copy. A distribution can have one origin or many. Common origins:

  • An Amazon S3 bucket (Simple Storage Service — object storage) for static files like images, CSS, and JavaScript.
  • An Application Load Balancer (ALB) or an EC2 instance (Elastic Compute Cloud — a virtual server) for a dynamic application.
  • Any public HTTP server, even one outside AWS (a “custom origin”).

CloudFront fetches from the origin only on a cache miss (when no fresh copy is at the edge). Every other request is served from the edge — that is the whole point.

Cache behaviors

A cache behavior maps a URL path pattern to a specific origin, plus the caching rules for that path. Every distribution has one default behavior (path pattern *) that catches everything not matched by a more specific rule. You then add ordered behaviors that are evaluated top to bottom; the first matching pattern wins.

This lets you route different paths to different origins from one domain. A classic setup:

Path patternOriginWhy
/static/*S3 bucketStatic assets, cache hard
/api/*ALB / API originDynamic responses, cache little or nothing
* (default)ALB / web originEverything else

When to use multiple behaviors: when one domain serves both heavily cacheable static files and barely-cacheable dynamic API responses. Use separate behaviors so you can cache /static/* aggressively while leaving /api/* nearly uncached. When not to: if everything behaves the same, one default behavior is simpler.

Cache policies, origin request policies, and TTLs

Two policies control what CloudFront sends and what it remembers:

  • Cache policy — defines the cache key (which parts of the request make a response “unique”) and the TTLs (Time To Live — how long a cached copy stays fresh before CloudFront re-checks the origin). The cache key can include selected headers, query strings, and cookies.
  • Origin request policy — defines what CloudFront forwards to the origin. This is separate on purpose: you can forward a header to your app for logging without it becoming part of the cache key.

AWS provides managed cache policies so you rarely start from scratch:

Managed cache policyCaches based onUse for
CachingOptimizedURL path only (no headers/cookies/query strings)Static assets — best hit ratio
CachingDisabledNothing is cachedAPIs, fully dynamic content
CachingOptimizedForUncompressedObjectsURL path, no compression negotiationAlready-compressed media

TTLs have three values: Minimum, Maximum, and Default. If your origin sends a Cache-Control: max-age=... header, CloudFront honors it within the min/max bounds. If the origin sends nothing, the Default TTL applies.

The cache key gotcha (the big one)

The cache key is what makes or breaks your hit ratio. Every distinct cache key value is a separate cached object. If your cache key includes a header, query string, or cookie that varies a lot but does not actually change the response, you “shatter” the cache: the same content gets stored thousands of times under different keys, and most requests become cache misses.

Examples of cache-shattering keys:

  • A session-id cookie in the key → one cached copy per user.
  • A utm_source marketing query string in the key → one copy per ad campaign, for identical HTML.
  • A User-Agent header in the key → one copy per browser version.

Warning: Putting unnecessary headers, cookies, or query strings in the cache key is the most common reason a CloudFront hit ratio is terrible. Tune the cache key to include only the things that genuinely change the response (for example, Accept-Encoding for gzip vs Brotli, or a ?lang=fr parameter that returns translated content). When in doubt, exclude it.

How to: create a cache policy

Console steps

  1. Open the CloudFront console and choose Policies in the left menu, then the Cache tab.
  2. Click Create cache policy.
  3. Set a Name, e.g. static-assets-cache.
  4. Under TTL settings, set Minimum 1, Default 86400 (1 day), Maximum 31536000 (1 year).
  5. Under Cache key settings:
    • Headers: choose None (or include only Accept-Encoding).
    • Query strings: None.
    • Cookies: None.
  6. Click Create, then attach it to a behavior in your distribution.

AWS CLI

aws cloudfront create-cache-policy --cache-policy-config '{
  "Name": "static-assets-cache",
  "DefaultTTL": 86400,
  "MaxTTL": 31536000,
  "MinTTL": 1,
  "ParametersInCacheKeyAndForwardedToOrigin": {
    "EnableAcceptEncodingGzip": true,
    "EnableAcceptEncodingBrotli": true,
    "HeadersConfig": { "HeaderBehavior": "none" },
    "QueryStringsConfig": { "QueryStringBehavior": "none" },
    "CookiesConfig": { "CookieBehavior": "none" }
  }
}'

Output:

{
    "Location": "https://cloudfront.amazonaws.com/2020-05-31/cache-policy/a1b2c3d4-1111-2222-3333-444455556666",
    "ETag": "E2QWRUHEXAMPLE",
    "CachePolicy": {
        "Id": "a1b2c3d4-1111-2222-3333-444455556666",
        "CachePolicyConfig": { "Name": "static-assets-cache", "DefaultTTL": 86400 }
    }
}

You can check whether requests are hitting the cache by reading the x-cache response header:

curl -sI https://d123abcdef.cloudfront.net/static/logo.png | grep -i x-cache

Output:

x-cache: Hit from cloudfront

Hit from cloudfront means it was served from the edge; Miss from cloudfront means CloudFront fetched from the origin.

Attaching a policy to a behavior (Terraform)

resource "aws_cloudfront_distribution" "site" {
  # ... origins and other settings ...

  ordered_cache_behavior {
    path_pattern           = "/static/*"
    target_origin_id       = "s3-assets"
    viewer_protocol_policy = "redirect-to-https"
    cache_policy_id        = aws_cloudfront_cache_policy.static.id
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
  }
}

Cost note

CloudFront charges for data transfer out to the internet and per 10,000 requests, but cache hits never touch your origin, so a high hit ratio cuts both origin compute/transfer costs and origin load. Cache policies, behaviors, and TTLs themselves are free — tuning the cache key is pure savings. Invalidations are free for the first 1,000 paths per month, then about $0.005 per path, so prefer versioned filenames (logo.v2.png) over invalidating.

Best Practices

  • Use the managed CachingOptimized policy for static assets and CachingDisabled for APIs before building custom policies.
  • Keep the cache key minimal — include a header, cookie, or query string only when it actually changes the response.
  • Route static and dynamic paths to different origins with ordered behaviors so each can have its own caching rules.
  • Set sensible TTLs, but let the origin’s Cache-Control header drive freshness when you can.
  • Forward data your origin needs via an origin request policy, not the cache policy, so logging needs do not shatter the cache.
  • Use versioned asset filenames instead of frequent invalidations.
  • Monitor the cache hit ratio in CloudFront reports; a low ratio almost always means an over-broad cache key.
Last updated June 15, 2026
Was this helpful?