CDN Caching Explained: Cache-Control, TTL & Cache Keys 2026

CDN Caching Explained: Cache-Control, TTL, Cache Keys, HIT vs MISS and Edge Caching Architecture

Caching is one of the most powerful—and frequently misunderstood—technologies behind modern web performance.

A website may run on extremely powerful servers, use a high-performance database, and connect through a global cloud network, yet still deliver unnecessary requests to its origin infrastructure because its caching architecture is poorly designed.

A well-designed cache changes the request path from:

User
  │
  ▼
Internet
  │
  ▼
Application Server
  │
  ▼
Database
  │
  ▼
Application Server
  │
  ▼
User

into:

User
  │
  ▼
Nearby CDN
  │
  ▼
CACHE HIT
  │
  ▼
Response

The difference can be enormous.

Caching can reduce:

  • application latency;
  • origin traffic;
  • database queries;
  • serverless function executions;
  • bandwidth between infrastructure layers;
  • compute consumption;
  • infrastructure cost;
  • load during traffic spikes.

But caching also introduces difficult engineering questions.

How long should content remain cached?

What exactly identifies one cached object?

Should cookies affect the cache?

What happens when content changes?

Should stale content be served while a new response is generated?

How do you prevent personalized data from entering a shared CDN cache?

And how can a poorly designed cache key create either terrible performance or a security vulnerability?

This guide explains CDN caching architecture, browser caching, Cache-Control, TTL, cache keys, cache HIT vs MISS, stale-while-revalidate, stale-if-error, cache invalidation, cache purging, cache poisoning, personalized content, dynamic APIs, and practical caching strategies for AWS CloudFront, Cloudflare, Vercel, and Netlify.

For the broader distributed-computing layer surrounding caching, start with our Edge Computing Explained guide.

What Is Caching?

Caching stores a reusable copy of data so that future requests can be answered without repeating the complete operation required to generate the original response.

Imagine an application that generates a product page.

Without caching:

Request
   │
   ▼
Application
   │
   ▼
Database Query
   │
   ▼
Template Rendering
   │
   ▼
Response

Every visitor repeats this workflow.

With caching:

First Request
     │
     ▼
Application
     │
     ▼
Database
     │
     ▼
Generate Response
     │
     ▼
Store in Cache

Later requests become:

Request
   │
   ▼
Cache
   │
   ▼
Response

The expensive work has already been performed.

Caching is therefore not simply about storing files.

It is about avoiding repeated work.


Where Can Caching Exist?

A modern application may contain several caches simultaneously.

User
 │
 ▼
Browser Cache
 │
 ▼
CDN Cache
 │
 ▼
Reverse Proxy Cache
 │
 ▼
Application Cache
 │
 ▼
Database Cache
 │
 ▼
Database

Each layer solves a different problem.

For example:

Browser Cache
→ Avoid network requests entirely

CDN Cache
→ Avoid requests reaching the origin

Reverse Proxy Cache
→ Avoid application execution

Application Cache
→ Avoid expensive calculations

Database Cache
→ Avoid expensive data retrieval

This means “the cache” is rarely one thing.

Production applications frequently contain a cache hierarchy.


How CDN Caching Works

A CDN distributes cached content across geographically distributed infrastructure.

Without a CDN:

Europe User
     │
     │ Long Network Path
     ▼
US Origin

With a CDN:

                   Origin
                     │
              Global CDN
          ┌──────────┼──────────┐
          ▼          ▼          ▼
       Europe       Asia      America
          │          │          │
        Users      Users      Users

When a request arrives, the CDN determines whether it already has a valid copy of the requested object.

Conceptually:

Request
   │
   ▼
CDN Edge
   │
   ▼
Cache Lookup
   │
   ├── HIT ─────────────► Response
   │
   └── MISS
          │
          ▼
       Origin
          │
          ▼
       Response
          │
          ▼
       Cache Copy
          │
          ▼
         User

AWS describes this behavior directly: when CloudFront finds a valid object corresponding to the generated cache key, the request becomes a cache hit and can be served from the edge rather than the origin.

This reduces both viewer latency and origin load.


Browser Cache vs CDN Cache

These two caches should not be confused.

A browser cache exists on the user’s device:

Browser
   │
   ▼
Local Cache

A CDN cache exists on shared network infrastructure:

Many Users
     │
     ▼
CDN Edge Cache

This difference matters because browser and shared-cache lifetimes often need to be different.

For example, you may want:

Browser Cache = 60 seconds

CDN Cache = 1 hour

Why?

Because you may want the CDN to reuse one generated response for thousands of users while still allowing individual browsers to check the CDN relatively frequently.

That distinction leads directly to max-age and s-maxage.


Cache HIT vs Cache MISS

Two of the most important caching terms are:

HIT

and:

MISS

A cache HIT means the cache already contains a valid response for the request.

Request
   │
   ▼
Cache
   │
   ▼
HIT
   │
   ▼
Response

A cache MISS means the requested object is not currently available as a valid cached response.

Request
   │
   ▼
Cache
   │
   ▼
MISS
   │
   ▼
Origin

The origin must then perform the work.


Why Cache HITs Matter

Suppose a dynamic page requires:

Application Processing = 80 ms
Database Query = 100 ms
API Call = 120 ms

Ignoring other overhead, generating the response may already require around:

300 ms

If the CDN can serve a cached copy in a fraction of that time, the entire application path changes.

More importantly, the backend does no work for that request.

One HIT can therefore save:

Compute
+
Database Work
+
API Work
+
Origin Bandwidth

Multiply this by millions of requests and caching becomes an infrastructure strategy rather than merely a frontend optimization.


What Is Cache Hit Ratio?

The cache hit ratio measures the proportion of eligible requests served from cache.

A simplified formula is:

Cache Hit Ratio =
Cache Hits
──────────────
Total Cache Requests
× 100

Suppose:

1,000,000 requests

produce:

850,000 HITs
150,000 MISSes

The cache hit ratio is:

85%

This means the origin avoids serving 850,000 of those requests directly.

AWS exposes CloudFront’s CacheHitRate as a metric representing the percentage of cacheable requests served from cache.


Why a Low Cache Hit Ratio Can Be Expensive

Consider two otherwise identical systems.

System A

1,000,000 requests
90% cache hit ratio

Origin requests:

≈ 100,000

System B

1,000,000 requests
20% cache hit ratio

Origin requests:

≈ 800,000

System B may require far more:

  • compute;
  • database capacity;
  • bandwidth;
  • function execution;
  • application scaling.

Improving caching can therefore sometimes deliver more value than adding servers.


What Is TTL?

TTL means:

Time To Live.

It defines how long a cached object can remain fresh before it requires expiration or revalidation.

Example:

TTL = 3600 seconds

means:

Cache object considered fresh
for 1 hour

During that period:

Request → Cache → HIT

After expiration, the cache may need to:

Revalidate

or:

Fetch New Response

depending on the caching strategy.


Long TTL vs Short TTL

Long TTL:

Long TTL
   │
   ├── More cache HITs
   ├── Less origin traffic
   └── Greater stale-content risk

Short TTL:

Short TTL
   │
   ├── Fresher content
   ├── More revalidation
   └── More origin traffic

Caching architecture therefore balances:

PERFORMANCE
     ↕
FRESHNESS

The correct balance depends on the content.


Cache-Control Explained

HTTP provides the Cache-Control response header to communicate caching behavior.

Example:

Cache-Control: public, max-age=3600

This tells compatible caches that the response can be cached and considered fresh for 3,600 seconds.

But Cache-Control supports much more than one TTL.

Important directives include:

public
private
max-age
s-maxage
no-cache
no-store
must-revalidate
immutable
stale-while-revalidate
stale-if-error

Understanding these directives is fundamental to CDN architecture.


public

Example:

Cache-Control: public, max-age=3600

public indicates that a response may be stored by shared caches.

That can include:

CDN
Proxy Cache
Other Shared Cache

This is suitable for responses intended to be shared across users.


private

Example:

Cache-Control: private, max-age=60

This indicates that the response is intended for a private cache such as the user’s browser rather than a shared CDN cache.

This is important for personalized content.

For example:

/dashboard

might contain:

Hello, Alice
Account Balance
Private Messages

That response must not accidentally become:

Shared CDN Cache
      │
      ▼
Another User

Personalized responses require careful caching rules.


no-store

Example:

Cache-Control: no-store

This tells caches not to store the response.

It can be appropriate for highly sensitive or immediately changing content.

For example:

Sensitive Financial Response
Authentication Response
One-Time Data

Not every dynamic page requires no-store, but truly sensitive responses should not enter shared caches accidentally.


no-cache Is Frequently Misunderstood

The name sounds like:

Never cache this.

That is not necessarily the correct interpretation.

no-cache generally means a stored response must be validated before being reused.

This is different from:

no-store

which means the response should not be stored.

That distinction matters when debugging HTTP caching.


max-age

max-age defines the freshness lifetime in seconds.

Example:

Cache-Control: public, max-age=300

means:

Fresh for 5 minutes

A browser or compatible cache may reuse the object during that window.


s-maxage

s-maxage is particularly important for CDN architecture because it targets shared caches.

Example:

Cache-Control: public, max-age=60, s-maxage=3600

Conceptually:

Browser:
60 seconds

Shared CDN:
3600 seconds

This lets architects control the two layers separately.

Vercel, for example, documents s-maxage as the duration a response remains fresh in its CDN cache.


A Practical max-age and s-maxage Strategy

Consider a blog article.

You might use:

Cache-Control: public, max-age=60, s-maxage=3600

The result is:

Browser
  │
  └── Reuse for 60 seconds

CDN
  │
  └── Reuse for 1 hour

This provides:

  • short browser caching;
  • aggressive shared caching;
  • reduced origin load.

The exact values should follow your publishing requirements.


immutable

Content-addressed assets are ideal candidates for extremely long caching.

For example:

app.8f912c.js
styles.a9321.css

If the content changes, the filename changes.

Therefore:

app.8f912c.js

can safely remain cached for a long time because a future deployment might generate:

app.5bc912.js

A common strategy is:

Cache-Control: public, max-age=31536000, immutable

This effectively says:

This exact URL should not change.

Vercel currently recommends this type of strategy for immutable hashed static assets.


stale-while-revalidate

One of the most useful modern caching patterns is:

stale-while-revalidate

Traditional expiration can create this sequence:

Cache Expires
     │
     ▼
Next User Waits
     │
     ▼
Origin Generates Response
     │
     ▼
Cache Updated

stale-while-revalidate changes the behavior:

Cache Becomes Stale
       │
       ▼
Request Arrives
       │
       ├── Serve Stale Response Immediately
       │
       └── Refresh Cache in Background

This can dramatically improve perceived performance.


stale-while-revalidate Example

Consider:

Cache-Control: public, s-maxage=60, stale-while-revalidate=300

Conceptually:

0–60 seconds
    │
    └── Fresh

60–360 seconds
    │
    ├── Can serve stale
    └── Revalidate in background

Netlify documents support for this pattern, allowing stale content to be returned while the cached response is refreshed in the background.

Vercel also supports stale-while-revalidate in its CDN caching model.


Why stale-while-revalidate Is Powerful

Suppose an API requires five seconds to generate a response.

Without SWR:

Cache Expired
     │
     ▼
User waits 5 seconds

With SWR:

Cache Expired
     │
     ├── User receives stale response quickly
     │
     └── Cache updates in background

This can work well for:

  • blog indexes;
  • product catalogs;
  • documentation;
  • public API responses;
  • semi-static pages.

It is less suitable when every response must reflect immediate state.


stale-if-error

Another useful directive is:

stale-if-error

Imagine:

CDN
 │
 ▼
Origin
 │
 X
500 Error

Without fallback:

User → 500

With an appropriate stale-if-error policy:

Origin Failure
      │
      ▼
Serve Older Cached Response

This turns caching into an availability mechanism as well as a performance mechanism.

Vercel currently supports stale-if-error for applicable CDN responses.

AWS also documents stale directives as part of cache-hit optimization strategies.


Cache Keys Explained

A cache cannot simply ask:

Do I have this page?

It needs a precise identifier.

That identifier is the cache key.

Conceptually:

Request
   │
   ▼
Generate Cache Key
   │
   ▼
Look Up Object

For example:

https://example.com/products

might produce one cache key.

But what about:

https://example.com/products?category=laptops

or:

https://example.com/products?category=phones

Should these produce the same cached response?

Probably not.

The cache key determines the answer.


Cache Key Components

Depending on platform and configuration, a cache key may involve:

Scheme
+
Hostname
+
Path
+
Query String
+
Headers
+
Cookies

But including everything is usually a mistake.

Why?

Because every additional dimension can fragment the cache.


Cache Key Explosion

Suppose a page is identical for everyone.

But you include:

User-Agent

in the cache key.

Different browsers produce many different User-Agent values.

Instead of:

/page
   │
   ▼
ONE cached object

you may create:

/page + Chrome
/page + Firefox
/page + Safari
/page + Chrome Mobile
/page + Edge
/page + thousands of variations

The result:

Cache Fragmentation
      │
      ▼
Lower HIT Ratio
      │
      ▼
More Origin Traffic

AWS specifically warns that highly variable headers such as User-Agent can generate many unnecessary cached copies and reduce cache efficiency.


The Golden Rule of Cache Keys

A useful principle is:

Include a request attribute in the cache key only when it can legitimately change the response.

If:

utm_source=facebook

does not change page content, there may be little value in creating a separate cached object for it.

If:

language=fr

does change content, language needs to be represented somehow.

Good cache architecture minimizes unnecessary variation without serving incorrect content.


Query Strings and Caching

Consider:

/article?id=100

and:

/article?id=200

If id changes the article, it must influence caching.

But tracking parameters may not:

/article?id=100&utm_source=google
/article?id=100&utm_source=newsletter

If both generate identical content but separate cache entries, cache efficiency falls.

Normalization can convert both into the logical equivalent:

/article?id=100

for caching purposes.

Cloudflare’s current Cache Rules allow query-string components to be included or excluded when constructing custom cache keys.


Cloudflare Cache Keys

Cloudflare currently documents a default cache key that includes the full URL—including scheme, host, URI and query string—plus several relevant request headers.

Cloudflare also supports custom cache keys that can control dimensions including:

Query Strings
Headers
Cookies
Host
User

This provides powerful control, but Cloudflare warns that additional variation can reduce the cache hit rate through cache sharding.

This is a fundamental CDN engineering trade-off:

More Specific Cache
        │
        ├── More Accurate Variants
        └── Fewer HITs

versus:

Simpler Cache
        │
        ├── More HITs
        └── Greater Risk of Incorrect Sharing

AWS CloudFront Cache Keys

AWS CloudFront also bases caching decisions on a cache key.

CloudFront’s default cache key uses the distribution domain and requested object path, while cache policies allow architects to include selected:

Query Strings
Headers
Cookies

when required.

AWS recommends including only the values needed to distinguish responses because unnecessary variation reduces the cache hit ratio.

That principle should guide CDN design regardless of provider.


Cookies and Caching

Cookies are particularly dangerous for cache efficiency.

Suppose every visitor has:

session_id=UNIQUE_VALUE

and that cookie becomes part of the cache key.

You effectively create:

User 1 → Cache Object 1
User 2 → Cache Object 2
User 3 → Cache Object 3
...

At that point:

Shared CDN Cache

starts behaving like:

Per-User Storage

and much of the CDN benefit disappears.

Worse, incorrect cookie handling can create privacy problems.


Personalized Content

Suppose:

/dashboard

generates:

Hello Alice

for Alice.

If that response enters a shared cache under only:

/dashboard

Bob could potentially receive Alice’s cached response.

This is catastrophic.

Therefore personalized content must use strategies such as:

private

or:

no-store

or carefully designed cache segmentation.

Never assume dynamic automatically means safe.

Caching rules must explicitly match the data model.


Separate Shared and Personalized Content

A better architecture can divide a page into:

Shared Content
      +
Personalized Content

For example:

Product Page
   │
   ├── Product Description → CDN Cache
   ├── Images → CDN Cache
   ├── Reviews → API Cache
   └── User Cart → Private/Dynamic

This allows most of the page to benefit from caching while sensitive state remains private.

This pattern is often better than disabling caching for the entire page.


Dynamic API Caching

APIs can also be cached.

Suppose:

GET /api/weather?city=london

takes:

500 ms

because it calls an external service.

If the response can tolerate 60 seconds of staleness:

Cache-Control: public, s-maxage=60

can transform:

10,000 API requests
       │
       ▼
10,000 backend executions

into something closer to:

10,000 requests
       │
       ├── Mostly CDN HITs
       │
       └── Occasional origin refresh

This can significantly reduce both latency and backend cost.


Do Not Cache Every API

An API such as:

GET /api/products

may be cacheable.

An API such as:

GET /api/my-bank-account

should obviously not be stored in a public shared cache.

Similarly:

POST /api/payment

is not a normal CDN-cache candidate.

Cache design must follow request semantics.


Cache Invalidation

Caching introduces one of computer science’s famous difficult problems:

How do you remove data when it is no longer valid?

Suppose:

/article

is cached for one hour.

You publish an update after five minutes.

Without invalidation:

Origin = New Article

CDN = Old Article

Users may continue seeing stale content.

Several strategies exist.


Strategy 1: Wait for TTL Expiration

Simple:

Publish
   │
   ▼
Wait
   │
   ▼
TTL Expires
   │
   ▼
New Version Fetched

Easy, but updates are delayed.


Strategy 2: Purge

Tell the CDN:

Delete cached /article

The next request becomes a MISS:

Request
   │
   ▼
MISS
   │
   ▼
Origin
   │
   ▼
New Content

This provides faster updates.


Strategy 3: Versioned URLs

Static assets can use content hashes:

style.a123.css

When content changes:

style.b456.css

No purge is required because the URL itself changes.

This is one of the strongest caching patterns for immutable assets.


Strategy 4: Cache Tags

Some platforms allow related objects to be tagged.

Example:

Tag: product-123

could identify:

/products/123
/api/products/123
/category/laptops

When the product changes, the application can invalidate related cache objects by tag rather than clearing everything.

This makes invalidation more precise.


Purge Everything Is Not Always a Good Strategy

Imagine a CDN containing:

1,000,000 cached objects

and only one article changes.

Purging the entire cache can create:

Mass Cache MISS
      │
      ▼
Traffic Spike to Origin

This can cause an origin-load surge known conceptually as a cache stampede.

Granular invalidation is usually preferable.


What Is a Cache Stampede?

Suppose a very popular cached object expires.

Immediately:

10,000 Requests
       │
       ▼
Expired Cache
       │
       ▼
Origin

If all requests independently regenerate the same response, the origin may be overwhelmed.

A better architecture performs request collapsing or revalidation:

10,000 Requests
       │
       ▼
Cache
       │
       ├── One Refresh → Origin
       │
       └── Others → Cached/Stale Response

Modern CDN systems use several techniques to mitigate this class of problem.


AWS CloudFront Caching Architecture

A simplified AWS architecture is:

Users
  │
  ▼
CloudFront Edge
  │
  ├── HIT ─────────► Response
  │
  └── MISS
       │
       ▼
     Origin

The origin could be:

Amazon S3
Application Load Balancer
EC2
API
External Server

AWS CloudFront cache policies control important caching characteristics such as:

  • TTL;
  • query strings;
  • cookies;
  • request headers;
  • compression settings.

AWS also exposes cache-hit statistics and origin-latency metrics through its monitoring stack.

For broader AWS edge execution architecture, see our Edge Computing Explained guide.


Cloudflare Caching Architecture

Cloudflare places its cache in front of proxied origins:

User
 │
 ▼
Cloudflare
 │
 ├── HIT ───────► User
 │
 └── MISS
       │
       ▼
     Origin

Cloudflare currently caches common static resources by default, while dynamic HTML is generally not cached by default unless configured through caching rules.

This distinction matters.

Installing a CDN does not automatically mean every HTML page is being cached.

Caching behavior must be verified.

Cloudflare provides Cache Rules for controlling which resources are eligible and how they should be cached.


Cloudflare Tiered Caching

A basic CDN can produce:

Edge A ──► Origin
Edge B ──► Origin
Edge C ──► Origin

A tiered architecture introduces an intermediate cache layer:

Edge A ──┐
Edge B ──┼──► Upper-Tier Cache ──► Origin
Edge C ──┘

Now multiple edge locations can reuse upstream cached content instead of independently contacting the origin.

Cloudflare currently provides Tiered Cache as a mechanism for reducing origin traffic by organizing cache access through multiple caching layers.

This is particularly valuable for origins with expensive or limited capacity.


Vercel CDN Caching

Vercel’s current CDN architecture automatically caches static files and allows dynamic responses—including applicable server-rendered pages and API responses—to be controlled using caching headers.

A simplified path is:

User
 │
 ▼
Vercel CDN
 │
 ├── HIT
 │    │
 │    ▼
 │  Response
 │
 └── MISS
      │
      ▼
   Function / Origin

Vercel supports:

Cache-Control
CDN-Cache-Control
Vercel-CDN-Cache-Control

for different caching scopes.

This makes it possible to separate:

Browser TTL

from:

Generic CDN TTL

and:

Vercel-specific TTL

when required.


Vercel Targeted Cache Control

For example:

Cache-Control: max-age=10
CDN-Cache-Control: max-age=60
Vercel-CDN-Cache-Control: max-age=3600

can conceptually establish different policies for:

Browser
   │
   └── 10 seconds

Other CDN
   │
   └── 60 seconds

Vercel CDN
   │
   └── 3600 seconds

Vercel’s current documentation defines Vercel-CDN-Cache-Control as its highest-priority targeted cache-control header, followed by CDN-Cache-Control, then standard Cache-Control.

This level of control becomes useful in multi-layer delivery architectures.


Netlify Caching

Netlify also distinguishes between static and dynamic caching.

Static deployment assets are cached globally and automatically invalidated when deployments change them.

Responses generated by Functions, Edge Functions, and proxies are not cached by default, but caching can be enabled using response headers.

Netlify supports:

Netlify-CDN-Cache-Control
CDN-Cache-Control
Cache-Control

along with directives including:

s-maxage
max-age
stale-while-revalidate

This allows application developers to control shared and browser caching independently.


A Practical Netlify Example

Netlify documents a pattern similar to:

Netlify-CDN-Cache-Control: public, max-age=60, stale-while-revalidate=120

The architecture becomes:

First Request
     │
     ▼
Generate
     │
     ▼
Cache

then:

Fresh Window
     │
     ▼
Serve Cache

and later:

Stale Window
     │
     ├── Serve Existing Response
     └── Refresh in Background

This is particularly useful for expensive API responses that can tolerate limited staleness.


CDN Caching vs Application Caching

Do not confuse:

CDN Cache

with:

Application Cache

A CDN cache stores HTTP responses.

Application caching might store:

Database Query Result
Rendered Fragment
Session Data
Computed Object
API Result

For example:

Request
   │
   ▼
CDN MISS
   │
   ▼
Application
   │
   ▼
Redis HIT
   │
   ▼
Response

Even though the CDN missed, the application still avoided a database query.

Multiple cache layers can work together.


CDN Cache vs Redis

These technologies solve different problems.

CDN
   │
   └── HTTP response delivery near users

while:

Redis
   │
   └── Fast data access near applications

A common architecture is:

User
 │
 ▼
CDN
 │
 ▼
Application
 │
 ▼
Redis
 │
 ▼
Database

Each layer progressively protects the one behind it.


Reverse Proxy Caching

A reverse proxy such as Nginx can also cache responses.

Architecture:

Internet
   │
   ▼
CDN
   │
   ▼
Nginx
   │
   ▼
Application

Caching can potentially exist at:

CDN
+
Nginx
+
Application

This can be powerful but increases complexity.

You need to understand:

Which layer owns freshness?

Which layer receives invalidation?

Which headers control each layer?

Otherwise, stale-content problems become difficult to diagnose.


Cache Poisoning

Caching can become a security vulnerability when an attacker manipulates a request in a way that causes a malicious or incorrect response to be stored and served to other users.

Conceptually:

Attacker
   │
   ▼
Manipulated Request
   │
   ▼
Origin Generates Bad Response
   │
   ▼
CDN Caches Response
   │
   ▼
Other Users Receive It

The danger is amplification.

A normal application vulnerability may affect one request.

A poisoned shared cache may distribute the result to many users.


Cache Key Security

Suppose the origin changes its response based on:

X-Custom-Language

but the CDN cache key ignores that header.

Request A:

X-Custom-Language: en

generates English.

Request B:

X-Custom-Language: fr

should generate French.

If both share the same cache key:

/page

one language can be incorrectly served to another request.

The broader security rule is:

Every request property that changes a cacheable response must be understood by the caching layer.

This is why cache-key design is both a performance and correctness problem.


Cache Deception

Another class of cache problems occurs when dynamic or private content is mistakenly interpreted as cacheable static content.

For example, poorly configured infrastructure might treat:

/account/profile.css

as a static-looking URL even though the backend routes it to private account data.

If a CDN caches the resulting response, sensitive data may be exposed.

Caching rules should therefore be based on application semantics—not merely filename patterns.


Caching Authentication Responses

As a general principle:

Authenticated User-Specific Response

should not be placed into a shared cache unless the architecture explicitly guarantees correct separation.

Vercel’s current CDN criteria, for example, exclude responses from normal caching under conditions including authorization-bearing requests, Set-Cookie, and private/no-store directives.

These safeguards help, but application developers must still understand the data being returned.


Negative Caching

Caching is not limited to successful responses.

Some architectures cache responses such as:

404 Not Found

for a limited period.

This can protect origins from repeated requests for nonexistent resources.

Example:

Bot requests missing.jpg
       │
       ▼
Origin returns 404
       │
       ▼
CDN caches 404 briefly

Later bot requests:

Bot
 │
 ▼
CDN
 │
 ▼
Cached 404

No origin work is required.

But long-lived negative caching can delay newly created resources from becoming visible.


Caching Redirects

Redirects can also be cached.

For example:

301 /old-page → /new-page

may be stored by browsers and intermediaries.

Be especially careful with permanent redirects because an incorrect 301 can persist longer than expected.

When testing migrations, temporary redirects may be safer until routing behavior is confirmed.


Caching and SEO

Caching indirectly affects SEO through:

  • Time to First Byte;
  • page availability;
  • Core Web Vitals;
  • crawler efficiency;
  • redirect behavior;
  • content consistency.

But incorrect caching can create SEO problems.

For example:

Old Title Cached
New Title at Origin

can delay search-engine discovery of updates.

More serious mistakes can create:

Wrong Canonical Header
Wrong Redirect
Wrong Language Variant
Wrong HTTP Status

at CDN scale.

Caching therefore needs to be part of technical SEO reviews.


WordPress and CDN Caching

A WordPress site adds another caching layer.

Typical architecture:

Visitor
  │
  ▼
CDN
  │
  ▼
Web Server Cache
  │
  ▼
PHP
  │
  ▼
Object Cache
  │
  ▼
Database

Possible layers include:

Cloudflare
LiteSpeed Cache
Nginx FastCGI Cache
Redis Object Cache
Browser Cache

Using all of them without understanding their roles can create invalidation problems.

A better approach is to define exactly which layer caches what.


Static Assets: Cache Aggressively

Versioned assets such as:

app.a71bc.js
style.12a9.css
logo.72e91.webp

are excellent long-term cache candidates.

A typical strategy:

Cache-Control: public, max-age=31536000, immutable

Because the filename changes when the content changes, old objects can remain cached safely.


HTML: Cache Carefully

HTML changes more frequently.

Possible strategy:

Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=600

Conceptually:

Browser
  │
  └── Revalidate frequently

CDN
  │
  └── Cache for 5 minutes
       +
       serve stale during refresh

This can work well for public content sites.

But actual values should reflect publishing frequency and correctness requirements.


API Responses: Cache Selectively

Consider:

/api/categories

Perhaps it changes once per day.

Aggressive caching may make sense.

But:

/api/stock-price

might require near-real-time data.

And:

/api/account

may contain private information.

Therefore:

API
 │
 ├── Public + Stable → Cache
 ├── Public + Semi-Dynamic → Short Cache / SWR
 ├── Real-Time → Little/No Cache
 └── Private → Never Shared Cache

Caching should be determined per route.


Practical Caching Strategy by Content Type

Content Example Strategy
Hashed JS/CSS Very long TTL + immutable
Images with versioned URLs Long TTL
Public blog posts CDN cache + controlled revalidation
Homepage Shorter CDN TTL
Public API data Route-specific TTL
User dashboard Private/no shared cache
Authentication response Usually no shared caching
Real-time data Minimal caching
404 responses Short negative cache where appropriate

These are architectural starting points—not universal values.


How to Debug Caching with curl

One of the most useful commands is:

curl -I https://example.com

Look for headers such as:

Cache-Control
Age
ETag
Last-Modified
Vary
CF-Cache-Status
X-Cache
x-vercel-cache

Exact headers vary by provider.

Run the command multiple times:

curl -I https://example.com
curl -I https://example.com
curl -I https://example.com

If caching works, provider-specific status headers may change from something equivalent to:

MISS

to:

HIT

depending on platform behavior.


Debug Cache-Control

Use:

curl -sI https://example.com | grep -i cache

This can quickly expose relevant cache headers.

For more detailed HTTP debugging:

curl -v https://example.com

Our Linux Network Tools Guide provides a broader toolkit for investigating network and HTTP behavior.


Test Different Query Strings

Try:

curl -I "https://example.com/page?id=1"

then:

curl -I "https://example.com/page?id=2"

and:

curl -I "https://example.com/page?id=1&utm_source=test"

Observe whether cache behavior changes.

This can reveal unnecessary cache fragmentation.


Test Cookies

You can also test:

curl -I \
  -H "Cookie: test=value" \
  https://example.com/page

Compare the result without the cookie.

If an irrelevant cookie destroys caching, cache efficiency may be unnecessarily reduced.

Be careful when testing authenticated systems.


Monitor Origin Traffic

Do not evaluate caching only from the browser.

A successful caching strategy should also change origin metrics.

Look for reductions in:

Requests
CPU
Database Queries
Function Invocations
Origin Bandwidth

If CDN HITs appear high but backend load remains unchanged, another application path may still be bypassing the cache.


Monitor Cache Hit Ratio

A production dashboard should include:

Cache HIT Rate
Cache MISS Rate
Origin Latency
Origin Request Count
Error Rate
Bandwidth

Changes in hit ratio can reveal configuration regressions.

For example:

Yesterday:
HIT = 92%

Today:
HIT = 24%

This deserves investigation even if the website still appears functional.


Common Causes of Low Cache Hit Ratio

Excessive Query Parameters

?page=1&utm_source=a
?page=1&utm_source=b
?page=1&utm_source=c

Cookies in Cache Keys

Unique session values create fragmentation.

Excessive Headers

Highly variable headers generate unnecessary variants.

Very Short TTLs

Objects expire before they can be reused.

Frequent Global Purges

The cache constantly becomes cold.

Low Traffic Per Object

Rarely requested objects naturally have fewer opportunities for HITs.

Personalized Responses

Some content genuinely cannot be shared.

A low hit ratio is therefore not automatically a configuration failure.

Context matters.


Cold Cache vs Warm Cache

A cold cache has little reusable content.

Request
   │
   ▼
MISS

A warm cache contains frequently requested objects.

Request
   │
   ▼
HIT

Immediately after:

Deployment
Purge
New Region Activation

cache HIT rates may temporarily fall.

This is normal.

The key question is whether frequently accessed objects become warm efficiently.


Origin Shield Architecture

Large CDNs may use another cache layer between edge locations and the origin.

Without shield:

Edge A ─────► Origin
Edge B ─────► Origin
Edge C ─────► Origin
Edge D ─────► Origin

With shield:

Edge A ──┐
Edge B ──┤
Edge C ──┼──► Shield Cache ──► Origin
Edge D ──┘

AWS provides CloudFront Origin Shield as an additional caching layer designed to improve cache efficiency and reduce requests reaching origins.

This can be useful for high-traffic architectures with many geographically distributed edge locations.


Caching and Autoscaling

Caching can dramatically reduce scaling requirements.

Without cache:

Traffic × 10
     │
     ▼
Origin Requests × 10
     │
     ▼
Need More Compute

With a high cache hit ratio:

Traffic × 10
     │
     ▼
CDN Absorbs Most Requests
     │
     ▼
Smaller Origin Increase

This complements the concepts in our Cloud Auto Scaling Explained guide.

Autoscaling handles increased compute demand.

Caching tries to prevent unnecessary compute demand from existing in the first place.


Caching and Load Balancing

Consider:

Internet
   │
   ▼
CDN
   │
   ▼
Load Balancer
   │
   ├── Server A
   ├── Server B
   └── Server C

Every cache HIT prevents a request from reaching:

Load Balancer
+
Server
+
Application

Caching therefore sits in front of load balancing as a traffic-reduction mechanism.

For the next infrastructure layer, see Cloud Load Balancing Explained.


Caching and Edge Computing

Caching and edge computing work especially well together.

Caching asks:

Can we reuse an existing response?

Edge computing asks:

If computation is required, can some of it happen closer to the user?

The combined architecture becomes:

User
 │
 ▼
Edge
 │
 ├── Cache HIT
 │     │
 │     ▼
 │   Response
 │
 └── Cache MISS
       │
       ▼
   Edge Function
       │
       ├── Can Generate?
       │
       └── Need Origin?
              │
              ▼
            Cloud

This is why the Edge Computing Explained article and this caching guide should be tightly linked.


Production Caching Architecture

A mature architecture might look like:

                     Global Users
                          │
                          ▼
                         DNS
                          │
                          ▼
                         CDN
                          │
              ┌───────────┴───────────┐
              │                       │
          CACHE HIT                CACHE MISS
              │                       │
              ▼                       ▼
           Response              Edge Logic
                                      │
                                      ▼
                                 Origin Shield
                                      │
                                      ▼
                                Load Balancer
                                      │
                           ┌──────────┴──────────┐
                           ▼                     ▼
                    Application A         Application B
                           │                     │
                           └──────────┬──────────┘
                                      ▼
                               Application Cache
                                      │
                                      ▼
                                   Database

Each layer has a specific responsibility.

Observability should measure each layer separately.


A Better Caching Decision Framework

For every route, ask:

Is the response identical for multiple users?

If yes, shared caching may be possible.

How quickly does the content change?

This determines TTL.

Can slightly stale content be tolerated?

If yes, consider:

stale-while-revalidate

Can stale content be served during origin failure?

If yes, consider:

stale-if-error

Which request values change the response?

These may belong in the cache key.

Is the content sensitive?

If yes, avoid shared caching.

How is content invalidated?

Choose:

TTL
Purge
Tags
Versioned URL
Revalidation

before production.


Caching Best Practices

A strong caching strategy usually follows these principles.

Cache immutable assets aggressively.

Use versioned filenames and long TTLs.

Keep cache keys as simple as correctness allows.

Every unnecessary dimension reduces cache efficiency.

Separate personalized data from shared content.

Do not sacrifice an entire page’s cacheability because one small component is user-specific.

Use stale content strategically.

stale-while-revalidate can provide both freshness and low latency.

Plan invalidation before deployment.

Do not wait until stale content reaches production to decide how purging works.

Measure cache HIT ratio.

If caching is important, it deserves monitoring.

Protect the cache from untrusted variation.

Cache keys are a security boundary as well as a performance configuration.

Test the origin separately.

A fast cache can hide a very slow backend until a purge occurs.


Frequently Asked Questions

What is CDN caching?

CDN caching stores copies of HTTP responses on geographically distributed infrastructure so that future requests can be served closer to users without repeatedly contacting the origin.

What is a cache HIT?

A cache HIT occurs when the CDN finds a valid cached object matching the request’s cache key and serves it without retrieving a new copy from the origin.

What is a cache MISS?

A cache MISS occurs when the required valid object is not available in cache, so the CDN must retrieve or generate it through the origin path.

What is cache hit ratio?

Cache hit ratio is the proportion of requests served successfully from cache relative to the relevant request population.

What is TTL in caching?

TTL, or Time To Live, controls how long a cached object remains fresh before expiration or revalidation.

What is a cache key?

A cache key is the identifier a cache uses to distinguish one cached object from another. Depending on configuration, it can incorporate the URL, query strings, headers, cookies, and other request attributes.

What does max-age mean?

max-age specifies how long a response can remain fresh in compatible caches.

What does s-maxage mean?

s-maxage specifies a freshness lifetime for shared caches such as CDNs and can override max-age for those caches.

What is stale-while-revalidate?

It allows a cache to serve a stale response immediately while refreshing that response asynchronously in the background.

What is stale-if-error?

It allows eligible stale content to be served when refreshing from the origin encounters an error, depending on CDN support and configuration.

Should HTML be cached?

Public HTML can often be cached effectively with appropriate TTL and invalidation rules. Personalized or highly dynamic HTML requires more careful handling.

Should API responses be cached?

Some public GET API responses are excellent cache candidates. Private, transactional, personalized, or real-time APIs may not be suitable for shared caching.

What is cache poisoning?

Cache poisoning occurs when manipulated input causes an incorrect or malicious response to enter a shared cache and potentially be served to other users.

Does a CDN automatically cache everything?

No. Cache eligibility varies by provider, HTTP method, headers, content type, response status, cookies, and configuration. For example, Cloudflare normally caches common static resources by default but does not simply cache all dynamic HTML automatically.


Final Thoughts

Caching changes the economics and architecture of web applications.

Without caching:

Every User
    │
    ▼
Every Request
    │
    ▼
Origin
    │
    ▼
Application
    │
    ▼
Database

With effective caching:

Millions of Requests
        │
        ▼
       CDN
        │
        ├── HIT
        │    │
        │    ▼
        │  Response
        │
        └── MISS
             │
             ▼
           Origin

The goal is not to maximize caching blindly.

The goal is to cache the correct data, for the correct duration, under the correct key, at the correct layer.

A good cache key improves reuse without mixing responses that should remain distinct.

A good TTL reduces unnecessary origin work without making content unacceptably stale.

stale-while-revalidate can reduce latency during refreshes.

stale-if-error can increase resilience during origin problems.

Versioned assets can remain cached for extremely long periods.

Private responses must remain private.

And cache invalidation must be designed as part of the application—not treated as an emergency operation after deployment.

This leads to an important architecture principle:

Fast Infrastructure
        +
Poor Cache Strategy
        =
Unnecessary Work

while:

Good Architecture
        +
Effective Caching
        =
Lower Latency
+
Lower Origin Load
+
Better Scalability
+
Potentially Lower Cost

Caching is therefore not merely a browser optimization.

It is a fundamental component of distributed systems architecture.

Continue with Edge Computing Explained to understand where distributed execution fits around the caching layer.

For frontend delivery platforms, see AWS Amplify vs Vercel vs Netlify vs Cloudflare Pages.

For the infrastructure underneath these systems, continue with Cloud Network Architecture: AWS vs Azure vs Google Cloud, Cloud Load Balancing Explained, and Cloud Auto Scaling Explained.

The CorgiNetwork architecture path now becomes:

Frontend Deployment → Edge Computing → CDN Caching → Load Balancing → Cloud Networking → Scaling → Infrastructure Automation.

Leave a Reply

Your email address will not be published. Required fields are marked *