Shaduf.
HTTP Cache Field Guide/CloudFront TTL policy and invalidation — audit refresh

Dated report · CloudFront-specific · 17 Sep 2026

CloudFront TTL policy and invalidation

When an Amazon CloudFront edge policy can override origin cache intent—and when to invalidate a stable URL instead of versioning a public asset.

Documentation-only. Retrieved 2026-09-17T12:42:25Z–2026-09-17T12:47:34Z. No live distribution, origin, endpoint, cache capture, browser/curl run, or invalidation was available; no observation is claimed.

Decision in one minute

  1. Private or sensitive response? Make the matching CloudFront behavior's MinTTL=0; use the current CachingDisabled/all-zero policy when edge caching is not intended. Choose private, no-cache only when a browser copy with validation is acceptable, otherwise no-store. A positive minimum is a release-blocking red flag.
  2. Frequently changing public asset? Publish a new fingerprinted URL, update every HTML/bootstrap/CSS/JS reference, and normally skip invalidation. Keep the old asset while references propagate or rollback remains possible.
  3. Stable HTML or urgent correction? Publish safely, invalidate the narrow CloudFront path, and record the invalidation ID until its status is Completed. Check again from a fresh client; completion is not a universal downstream purge.

Review cache-key and privacy safety separately; this report does not repeat that tutorial.

Status: observed = none. The examples below are synthetic, documentation-grounded outcomes for a named standard CloudFront distribution—not measurements of D-SYNTH-1, B-public, or B-account.

Scope and claim labels

What this covers

The model assumes a standard CloudFront distribution, the cache behavior that matches the request, a current CachePolicyId, and a cacheable GET/HEAD response. Let m, d, and M be that policy's effective minimum, default, and maximum TTL.

A response-headers policy is a separate viewer-response layer. A Cache-Control header added there controls browser caching; it does not set CloudFront's edge TTL.

What it does not cover

This is not a claim about every AWS product, CDN, browser, or proxy. It excludes custom-error caching, signed URL/cookie behavior, Origin Shield/REC internals, rewrites, and other features that can add controls.

Legacy ForwardedValues and behavior TTL fields are deprecated; inspect the effective mechanism rather than mixing legacy settings with a cache policy.

Normative RFC meaning CloudFront-specific AWS product/configuration rule Browser-facing private-cache interpretation Recommendation / inference operational conclusion Observed · none no live result in this report

Origin directives pass through CloudFront TTL bounds before reaching a browser An origin supplies max-age, s-maxage, Expires, or privacy directives. The matching CloudFront policy applies minimum, default, and maximum TTL bounds to the shared edge. The viewer can receive a separate browser-facing max-age, so the viewer header alone does not reveal the edge TTL. Origin response max-age=N / s-maxage=S Expires or privacy intent source signal, not the whole policy Matching CloudFront policy Minimum m · Default d · Maximum M floordefaultceiling edge TTL = bounded origin result m > 0 can floor no-store/private/no-cache Shared edge reuse / validate nominal freshness not a hit guarantee Viewer browser / private cache max-age can differ
The key boundary: CloudFront's edge TTL is bounded by the matching policy; a viewer-facing max-age is a separate browser instruction. This explanatory diagram is synthetic, not observed.

Precedence matrix: what wins?

Expected documentation outcomes for a cacheable response. clamp(x,m,M) means max(m,min(x,M)); no row is a live CloudFront measurement.

Origin signalCloudFront edge resultViewer / browser readingLabel
public, max-age=N, m=0min(N,M); an explicit freshness value displaces d.Browser uses max-age=N if delivered unchanged.CloudFront Browser
max-age=N, m>0clamp(N,m,M): the minimum can raise a shorter origin lifetime; the maximum caps a longer one.Viewer header can still say max-age=N; a response-headers policy may change it.CloudFront
max-age=N, s-maxage=SUse S for shared-edge freshness, bounded by m/M; RFC 9111 makes s-maxage override max-age/Expires in a shared cache.Browser uses max-age=N, not s-maxage=S.Normative CloudFront
No Cache-Control and no Expiresd when m=0; max(m,d) when m>0. All-zero settings are the documented disabled-caching case.No explicit browser lifetime is established; browser behavior can differ.CloudFront Browser
Expires only, or max-age plus Expiresmax-age wins when present; otherwise the future Expires value is bounded by policy. s-maxage wins for a shared cache.max-age wins in the browser too; a past Expires is stale, not a purge command.Normative CloudFront
no-cache, no-store, or private, m=0CloudFront documents respect for the origin directive. RFC 9111 distinguishes validation (no-cache) from no intentional storage (no-store) and shared-cache exclusion (private).Those fields retain their browser-facing meanings if delivered to the viewer.Normative CloudFront
The same privacy directive, m>0AWS explicitly documents caching for at least m despite no-cache, no-store, or private. This is a CloudFront override, not a new HTTP rule.A browser may still receive no-store/private; that header is not evidence the edge did not store.CloudFront override
s-maxage=S without max-ageRFC 9111 gives a shared cache S; CloudFront lists s-maxage as an edge input. Check this exact header shape in the named behavior rather than generalizing the product result.s-maxage is not a private-browser lifetime.Inference

TTL is not residency. An object can be evicted before expiration, and a selected TTL does not prove a hit, storage, POP, key safety, or the response actually delivered to a viewer.

Synthetic arithmetic

Effective policyOrigin signalExpected edge resultBrowser value
m=30, d=60, M=300max-age=1030s (minimum floor)10s if delivered
samemax-age=120120s120s
samemax-age=900300s (maximum cap)900s
samemax-age=600, s-maxage=1030s (s-maxage floored by m)600s
sameno freshness header60s (d)Not established

These are calculations from the documented rule, not a configured distribution or a cache capture.

The synthetic /account red flag

Imagine B-account returns a personalized body for /account with Cache-Control: private, no-store. If the matching CloudFront behavior has MinTTL=30, AWS documents that the edge can cache it for at least 30 seconds despite both directives. With a positive relevant TTL, AWS also documents serving an object previously obtained from the origin when the origin is unreachable. A later viewer could therefore receive an old personalized representation. AWS documents one mitigation for that failure mode: Cache-Control: stale-if-error=0 makes CloudFront return an error instead of serving the previously obtained object; it does not replace MinTTL=0/CachingDisabled and is not a general privacy fix.

This is a synthetic risk illustration, not an observed leak. Review the actual path pattern/order, CachePolicyId, MinTTL/DefaultTTL/MaxTTL, response-headers policy, and origin request/key design. See the cache-key/privacy report for the separate variant-safety review. Invalidation is not a privacy repair for copies held by browsers, service workers, proxies, or other layers.

Choose the deployment path

Private / sensitive response

Path: user-specific? → MinTTL=0CachingDisabled/all-zero when edge caching is not intended → private, no-cache for browser-local validated retention or no-store when intentional HTTP-cache retention is unacceptable.

  • Review authorization, redaction, response headers, and request/key inputs independently.
  • Treat a positive minimum or a positive default with absent origin headers as a release blocker.
  • Contain each relevant layer if exposure occurred; do not call invalidation a universal purge.

Public / versioned content

Path: changing asset? → publish /assets/app.<hash>.js → update every reference → retain the old URL through propagation/rollback. AWS recommends versioning frequently updated files and documents it as less expensive than invalidation.

  • Stable /index.html that must change now: publish safely, invalidate the exact path, then poll to Completed.
  • Versioning does not delete old URLs; invalidate/remove old paths only when that edge object itself must be refreshed or removed.
  • Neither route clears browser history/bfcache, service workers, corporate proxies, other CDNs, or uninstrumented hops.
Versioned URL and invalidation are different deployment paths A deployment change branches into a versioned URL path for frequently changing assets or a narrow invalidation path for a stable URL. Both end with a fresh-client check because downstream copies are outside CloudFront invalidation scope. Deploy change what must update? Versioned URL new hash/name → update refs retain old URL for rollback usually no invalidation Path invalidation exact stable path → ID poll InProgress → Completed CloudFront edge scope only Check from a fresh client new references, status, headers, named hop downstream old copies may remain
Versioning changes the target; invalidation removes a selected CloudFront edge entry. Neither is evidence that every viewer has discarded an old URL.

If you do invalidate: scope and status

CheckCloudFront-specific ruleOperational boundary
PathAPI paths are distribution-relative, require a leading /, and are case-sensitive. A * matches zero or more characters and must be final.Use /index.html or another narrow path; use /* only with intentional blast radius.
Query strings / variantsIf query strings are in the cache key, include the query or use an appropriate trailing wildcard such as /assets/app.js*. Cookies/headers create variants that a path invalidation clears together, not selectively.If a viewer-request function rewrites the URI, invalidate both viewer and rewritten paths; include slash/no-slash forms when both are requested.
Completion evidenceCreateInvalidation returns an ID and InProgress; record the ID, caller reference, and paths, then poll GetInvalidation/ListInvalidations until Completed.Completed means the named CloudFront operation finished—not that a browser, proxy, service worker, or other CDN purged its copy. Invalidation cannot be canceled after submission.
Cost / quotaAWS's current documentation lists 150 paths/tags per second, one wildcard invalidation per second, and the first 1,000 invalidation paths per AWS account per month free; later paths are charged. A wildcard counts as one billed path.Confirm current account pricing and quotas before a large purge; this report did not measure usage.

A viewer request with Cache-Control: no-cache or Pragma is not a CloudFront purge mechanism; AWS documents that CloudFront ignores those viewer headers for forcing an origin fetch.

What an authorized verification would record

  1. Read the effective behavior: path order, CachePolicyId, m/d/M, origin-request/key inputs, and response-headers policy.
  2. Use a sanitized test origin with a redacted marker A/B and counter; hold host, method, path, query, redirects, and client inputs fixed. Capture body marker/hash, status, Cache-Control, Age, Via, ETag/Last-Modified, and any named Cache-Status or CloudFront Server-Timing.
  3. For invalidation, save the API ID/path list and poll to Completed; for versioning, test the new URL and separately note that the old URL may still exist.

Not executed here: configuration read, origin marker/counter, cold/warm repeat, browser/fresh-client comparison, header capture, invalidation creation/status poll, versioned deploy, POP comparison, cost/quota usage, and live privacy or stale-deployment test.

Sources and limits

All sources below were retrieved or cross-checked during the stated window. AWS links describe CloudFront behavior; RFC links describe HTTP semantics. No browser-specific source was used for the CloudFront claims.

Boundary: the report documents rules and a verification plan; it does not prove a real CloudFront object, hit, POP, downstream cache, privacy exposure, invalidation, or deployment outcome. A positive Age/Cache-Status/CloudFront diagnostic would be scoped to its named hop; absence would remain unknown.

Search published pools, pages, reports, and evidence.