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.
Decision in one minute
- Private or sensitive response? Make the matching CloudFront behavior's
MinTTL=0; use the currentCachingDisabled/all-zero policy when edge caching is not intended. Chooseprivate, no-cacheonly when a browser copy with validation is acceptable, otherwiseno-store. A positive minimum is a release-blocking red flag. - 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.
- 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
max-age is a separate browser instruction. This explanatory diagram is synthetic, not observed.Precedence matrix: what wins?
| Origin signal | CloudFront edge result | Viewer / browser reading | Label |
|---|---|---|---|
public, max-age=N, m=0 | min(N,M); an explicit freshness value displaces d. | Browser uses max-age=N if delivered unchanged. | CloudFront Browser |
max-age=N, m>0 | clamp(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=S | Use 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 Expires | d 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 Expires | max-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=0 | CloudFront 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>0 | AWS 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-age | RFC 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 policy | Origin signal | Expected edge result | Browser value |
|---|---|---|---|
m=30, d=60, M=300 | max-age=10 | 30s (minimum floor) | 10s if delivered |
| same | max-age=120 | 120s | 120s |
| same | max-age=900 | 300s (maximum cap) | 900s |
| same | max-age=600, s-maxage=10 | 30s (s-maxage floored by m) | 600s |
| same | no freshness header | 60s (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=0 → CachingDisabled/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.htmlthat must change now: publish safely, invalidate the exact path, then poll toCompleted. - 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.
If you do invalidate: scope and status
| Check | CloudFront-specific rule | Operational boundary |
|---|---|---|
| Path | API 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 / variants | If 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 evidence | CreateInvalidation 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 / quota | AWS'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
- Read the effective behavior: path order,
CachePolicyId,m/d/M, origin-request/key inputs, and response-headers policy. - 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 namedCache-Statusor CloudFrontServer-Timing. - 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
Normative HTTP sources
- RFC 9111 §5.2.2.10 · s-maxage and §5.3 · Expires
- RFC 9111 §5.2.2.4 · private, §5.2.2.5 · no-store, and §5.2.2.6 · no-cache
- RFC 9111 §3 · storing responses and §4 · constructing responses
- RFC 9211 §2 · Cache-Status · RFC 9110 §7.6.3 · Via
- RFC 9110 §13.2.2 · conditional requests and §15.4.5 · 304 Not Modified
CloudFront documentation
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.