[Medium] π HTTP Caching
1. What is HTTP caching and why is it important?β
What is HTTP caching? Why is it important?
HTTP caching is a technique that stores HTTP responses temporarily in clients (browsers) or intermediate servers, so later requests can reuse cached data instead of hitting the origin again.
Cache vs Temporary Storageβ
In Chinese technical context, these two terms are often mixed, but they represent different purposes.
Cacheβ
Definition: a stored copy used for performance optimization, focusing on reuse and faster access.
Characteristics:
- β Goal: performance improvement
- β Data is expected to be reused
- β Explicit expiration/revalidation strategy
- β Usually a copy of original data
Examples:
// HTTP Cache - cache API responses
Cache-Control: max-age=3600 // cache 1 hour
// Memory Cache - cache computation result
const cache = new Map();
function fibonacci(n) {
if (cache.has(n)) return cache.get(n); // reuse cache
const result = /* compute */;
cache.set(n, result);
return result;
}
Temporary Storageβ
Definition: data stored for temporary workflow needs, emphasizing short-lived lifecycle.
Characteristics:
- β Goal: temporary retention
- β Reuse is optional
- β Usually shorter lifecycle
- β May hold intermediate state
Examples:
// sessionStorage - temporary form data
sessionStorage.setItem('formData', JSON.stringify(form));
// temporary upload file path
const tempFile = await uploadToTemp(file);
await processFile(tempFile);
await deleteTempFile(tempFile);
Comparisonβ
| Feature | Cache | Temporary Storage |
|---|---|---|
| Primary goal | Performance | Temporary retention |
| Reuse | Yes, often repeated | Not guaranteed |
| Lifecycle | Policy-driven | Usually short |
| Typical use | HTTP cache, memory cache | sessionStorage, temp files |
| English term | Cache | Temp / Temporary / Buffer |
Practical distinctionβ
// ===== Cache scenarios =====
// 1) HTTP cache: reuse API response
fetch('/api/users').then((response) => response.json());
fetch('/api/users').then((response) => response.json());
// 2) memoization cache
const memoize = (fn) => {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
};
// ===== Temporary storage scenarios =====
// 1) save draft on unload
window.addEventListener('beforeunload', () => {
sessionStorage.setItem('formDraft', JSON.stringify(formData));
});
// 2) temporary upload processing
async function handleUpload(file) {
const tempPath = await uploadToTempStorage(file);
const processed = await processFile(tempPath);
await deleteTempFile(tempPath);
return processed;
}
// 3) temporary intermediate results
const tempResults = [];
for (const item of items) {
tempResults.push(process(item));
}
const final = combine(tempResults);
In web developmentβ
// HTTP Cache (long-lived, reusable)
Cache-Control: public, max-age=31536000, immutable
// sessionStorage (temporary, tab-scoped)
sessionStorage.setItem('tempData', data)
// localStorage (persistent storage, not primarily a cache optimization layer)
localStorage.setItem('userPreferences', prefs)
Why this distinction mattersβ
- Design decisions:
- Need performance optimization -> cache
- Need temporary persistence -> temp storage
- Resource management:
- Cache focuses on hit rate and expiration strategy
- Temp storage focuses on cleanup timing and size limits
- Interview clarity:
- Performance question -> discuss caching strategy
- Temporary data question -> discuss temp storage strategy
This article mainly focuses on cache, especially HTTP caching.
Benefits of cachingβ
- Fewer network requests
- Lower server load
- Faster page loads
- Lower bandwidth usage
- Better user experience
Browser cache layersβ
βββββββββββββββββββββββββββββββββββββββ
β Browser Cache Layers β
βββββββββββββββββββββββββββββββββββββββ€
β 1. Memory Cache β
β - Fastest, small capacity β
β - Cleared with tab/session end β
βββββββββββββββββββββββββββββββββββββββ€
β 2. Disk Cache β
β - Slower, larger capacity β
β - Persistent storage β
βββββββββββββββββββββββββββββββββββββββ€
β 3. Service Worker Cache β
β - Fully controlled by app β
β - Enables offline behavior β
βββββββββββββββββββββββββββββββββββββββ
2. What are the HTTP caching strategies?β
What caching strategies exist in HTTP?
Strategy categoriesβ
HTTP Caching Strategies
βββ Strong Cache (Fresh)
β βββ Cache-Control
β βββ Expires
βββ Validation Cache (Negotiation)
βββ Last-Modified / If-Modified-Since
βββ ETag / If-None-Match
1. Strong cache (fresh cache)β
Behavior: browser serves from local cache directly without sending request to origin.
Cache-Control (HTTP/1.1)β
Cache-Control: max-age=3600
Common directives:
// 1) max-age: freshness lifetime in seconds
Cache-Control: max-age=3600
// 2) no-cache: allow caching but require revalidation before reuse
Cache-Control: no-cache
// 3) no-store: do not cache at all
Cache-Control: no-store
// 4) public: cacheable by browser/CDN/proxy
Cache-Control: public, max-age=31536000
// 5) private: browser cache only
Cache-Control: private, max-age=3600
// 6) immutable: content will not change during freshness lifetime
Cache-Control: public, max-age=31536000, immutable
// 7) must-revalidate: once stale, must revalidate
Cache-Control: max-age=3600, must-revalidate
Expires (HTTP/1.0, legacy)β
Expires: Wed, 21 Oct 2025 07:28:00 GMT
Issues:
- Uses absolute time
- Depends on client clock correctness
- Mostly replaced by
Cache-Control
2. Validation cache (negotiation)β
Behavior: browser asks server whether resource changed.
Last-Modified / If-Modified-Sinceβ
# first response
Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT
# subsequent request
If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT
Flow:
- First request: server sends
Last-Modified - Next request: browser sends
If-Modified-Since - Unchanged: server returns
304 Not Modified - Changed: server returns
200 OK+ new body
ETag / If-None-Matchβ
# first response
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
# subsequent request
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Advantages:
- More precise than
Last-Modified - Content-driven (hash or version token)
- Can detect changes not visible in second-level timestamps
Last-Modified vs ETagβ
| Feature | Last-Modified | ETag |
|---|---|---|
| Precision | Second-level | Content token/hash, more precise |
| Cost | Lower | May require extra computation |
| Good for | General static files | Exact validation control |
| Priority | Lower | Higher (ETag preferred when both exist) |
3. How does browser caching work?β
What is the browser cache workflow?
Full workflowβ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser Resource Request Flow β
ββββββββββββββββββββββββββββββββββββββββββββββββ
β
1. Check Memory Cache
β
βββββββββ΄βββββββββ
β Hit cache? β
βββββββββ¬βββββββββ
Yes β No
β
2. Check Disk Cache
β
βββββββββ΄βββββββββ
β Hit cache? β
βββββββββ¬βββββββββ
Yes β No
β
3. Check Service Worker
β
βββββββββ΄βββββββββ
β Hit cache? β
βββββββββ¬βββββββββ
Yes β No
β
4. Check freshness
β
βββββββββ΄βββββββββ
β Is stale? β
βββββββββ¬βββββββββ
Yes β No
β
5. Revalidate with server
β
βββββββββ΄βββββββββ
β Modified? β
βββββββββ¬βββββββββ
Yes β No (304)
β
6. Request new content
β
βββββββββ΄βββββββββ
β Return 200 β
β with new body β
ββββββββββββββββββ
Practical exampleβ
// first request
GET /api/data.json
Response:
200 OK
Cache-Control: max-age=3600
ETag: "abc123"
{ data: "..." }
// within 1 hour
// strong cache hit -> local use, no request
// status shown by devtools: from disk cache or from memory cache
// after 1 hour
GET /api/data.json
If-None-Match: "abc123"
// unchanged
Response:
304 Not Modified
// changed
Response:
200 OK
ETag: "def456"
{ data: "new data" }
4. What are common caching strategies?β
Common practical caching strategies
1. Long-lived static assetsβ
// HTML: no long cache, always validate
Cache-Control: no-cache
// CSS/JS with hash: long immutable cache
Cache-Control: public, max-age=31536000, immutable
// filename: main.abc123.js
Principle:
- HTML should stay fresh to reference latest asset hashes
- Hashed static assets can be cached for a long time
- Content change -> filename change -> new download
2. Frequently updated resourcesβ
// API data: short cache + revalidation
Cache-Control: max-age=60, must-revalidate
ETag: "abc123"
3. Image strategiesβ
// user avatars: medium cache
Cache-Control: public, max-age=86400
// logo/icons: longer cache
Cache-Control: public, max-age=2592000
// dynamic images: validation
Cache-Control: no-cache
ETag: "image-hash"
4. Suggested policies by resource typeβ
const cachingStrategies = {
html: 'Cache-Control: no-cache',
staticWithHash: 'Cache-Control: public, max-age=31536000, immutable',
staticAssets: 'Cache-Control: public, max-age=2592000',
apiData: 'Cache-Control: private, max-age=60',
userData: 'Cache-Control: private, no-cache',
sensitive: 'Cache-Control: no-store',
};
5. Service Worker cachingβ
Service Worker caching
Service Worker gives full control over runtime caching and offline behavior.
Basic usageβ
// register Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
// sw.js
const CACHE_NAME = 'my-app-v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/main.js',
'/images/logo.png',
];
// install: precache static assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(urlsToCache);
})
);
});
// fetch: cache-first example
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});
// activate: cleanup old caches
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
return caches.delete(cacheName);
}
})
);
})
);
});
Common SW strategiesβ
1. Cache Firstβ
// best for static assets
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});
2. Network Firstβ
// best for API requests
self.addEventListener('fetch', (event) => {
event.respondWith(
fetch(event.request)
.then((response) => {
const responseClone = response.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseClone);
});
return response;
})
.catch(() => {
return caches.match(event.request);
})
);
});
3. Stale While Revalidateβ
// best for fast response + background update
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
const fetchPromise = fetch(event.request).then((networkResponse) => {
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, networkResponse.clone());
});
return networkResponse;
});
return cachedResponse || fetchPromise;
})
);
});
6. How to implement cache busting?β
How to implement cache busting?
Cache busting ensures users fetch the latest assets when content changes.
Method 1: filename hashing (recommended)β
// with Webpack/Vite
// output: main.abc123.js
// webpack.config.js
module.exports = {
output: {
filename: '[name].[contenthash].js',
},
};
<script src="/js/main.abc123.js"></script>
Pros:
- β New filename forces download
- β Old files remain cacheable
- β Industry best practice
Method 2: query versionβ
<script src="/js/main.js?v=1.2.3"></script>
<link rel="stylesheet" href="/css/style.css?v=1.2.3" />
Cons:
- β Some CDNs/proxies treat query-string caching differently
- β Manual version maintenance
Method 3: timestampβ
// common in development only
const timestamp = Date.now();
const script = document.createElement('script');
script.src = `/js/main.js?t=${timestamp}`;
document.body.appendChild(script);
Use case:
- development cache bypass
- not ideal for production
7. Common caching interview questionsβ
Common caching interview questions
Question 1: How to prevent HTML from being cached?β
Click to view answer
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
or HTML meta tags:
<meta
http-equiv="Cache-Control"
content="no-cache, no-store, must-revalidate"
/>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
Question 2: Why use ETag instead of only Last-Modified?β
Click to view answer
Advantages of ETag:
- More precise
- Content-based validation
- Avoids timestamp edge cases (re-deploy with same content)
- Better in distributed systems with unsynced clocks
Example:
// content unchanged, deployment time changed
// Last-Modified changes, but content is identical
ETag: 'hash-of-abc'; // stable if content unchanged
Question 3: difference between from disk cache and from memory cache?β
Click to view answer
| Feature | Memory Cache | Disk Cache |
|---|---|---|
| Storage | RAM | Disk |
| Speed | Very fast | Slower |
| Capacity | Smaller | Larger |
| Persistence | Usually short-lived | Persistent |
| Priority | Higher | Lower |
Typical loading order (conceptual):
1. Memory Cache
2. Service Worker Cache
3. Disk Cache
4. Revalidation / Network
Question 4: how to force browser reload resources?β
Click to view answer
Development:
// Hard Reload
// Disable cache in DevTools
const script = document.createElement('script');
script.src = `/js/main.js?t=${Date.now()}`;
Production:
// hashed filenames (best)
main.abc123.js
// version query
<script src="/js/main.js?v=2.0.0"></script>
// cache policy
Cache-Control: no-cache
Cache-Control: no-store
Question 5: how to implement offline cache in PWA?β
Click to view answer
// sw.js
const CACHE_NAME = 'pwa-v1';
const OFFLINE_URL = '/offline.html';
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll([
OFFLINE_URL,
'/styles/offline.css',
'/images/offline-icon.png',
]);
})
);
});
self.addEventListener('fetch', (event) => {
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request).catch(() => {
return caches.match(OFFLINE_URL);
})
);
}
});
Full PWA strategy often combines:
// 1) static assets: cache-first
// 2) API: network-first
// 3) images: cache-first
// 4) HTML navigation: network-first + offline fallback
8. Best practicesβ
Best practices
β Recommendedβ
// 1. HTML: no long cache to ensure latest entry document
Cache-Control: no-cache
// 2. CSS/JS with hash: long immutable cache
// filename example: main.abc123.js
Cache-Control: public, max-age=31536000, immutable
// 3. Images: medium/long cache
Cache-Control: public, max-age=2592000
// 4. API data: short cache + validation
Cache-Control: private, max-age=60
ETag: "api-response-hash"
// 5. Service Worker for offline support
β Avoidβ
// bad: long cache for HTML entry document
Cache-Control: max-age=31536000
// bad: relying only on Expires
Expires: Wed, 21 Oct 2025 07:28:00 GMT
// bad: no explicit cache headers
// bad: same policy for all resource types
Cache-Control: max-age=3600
Cache strategy decision treeβ
Is it static asset?
ββ Yes -> filename has hash?
β ββ Yes -> long immutable cache (max-age=31536000, immutable)
β ββ No -> medium/long cache (e.g. max-age=2592000)
ββ No -> Is it HTML?
ββ Yes -> no-cache
ββ No -> Is it API?
ββ Yes -> short cache + validation (max-age=60 + ETag)
ββ No -> decide by update frequency