6 min read
Web Backend Fundamentals Index
Tier 1 -- Core Web & API Basics
Authentication & Authorization
Tier 2 -- Communication Models
Tier 3 -- Production & API Hardening
Web Backend Fundamentals Index
Tier 1 -- Core Web & API Basics
Authentication & Authorization
Tier 2 -- Communication Models
Tier 3 -- Production & API Hardening
HTTP Caching and Headers
1. What It Is
caching is a mechanism that allows responses to be stored and reused, so the same data doesn't have to be fetched from the origin server on every request. It is controlled through headers that tell browsers, CDNs, and proxies what to cache, for how long, and when a cached response is stale.
Caching is one of the highest-leverage performance optimizations in backend systems. A well-cached API can serve millions of requests with trivial server load. Poor caching leads to redundant database queries, slow responses, and unnecessary infrastructure cost.
A backend API endpoint fetches user profile data from a database and is called thousands of times per minute, but the profile data rarely changes. After enabling HTTP caching with appropriate headers, the team notices database query volume drops by over 90%. What is the most direct reason for this reduction?
2. How It Works in Practice
The two types of caching
1. Expiration-based caching — the server tells the client how long a response is fresh.
HTTP/1.1 200 OK Cache-Control: max-age=3600
The client caches this response and reuses it for 3600 seconds (1 hour) without contacting the server.
2. Validation-based caching — the server sends a fingerprint; the client asks "has this changed?"
The server sends a fingerprint:
HTTP/1.1 200 OK ETag: "abc123" Last-Modified: Mon, 14 Apr 2025 10:00:00 GMT
On the next request, the client sends the fingerprint back:
GET /products/5 HTTP/1.1 If-None-Match: "abc123"
If unchanged, the server responds with 304 Not Modified and no body — saving bandwidth. If changed, it returns 200 with the new body and a new ETag.
Key Cache-Control directives
| Directive | Meaning |
|---|---|
max-age=N | Cache is fresh for N seconds |
no-cache | Must revalidate with the server before using cached response |
no-store | Never cache this response (sensitive data) |
private | Only the end user's browser may cache; CDNs must not |
public | Any cache (CDN, proxy) may store this response |
s-maxage=N | Override max-age for shared caches (CDNs) only |
must-revalidate | Once stale, must revalidate before serving — don't serve stale data |
immutable | Content will never change; skip revalidation even after max-age |
Real example for a static asset:
Cache-Control: public, max-age=31536000, immutable
One year, cached by CDN, never revalidated (used for hashed filenames like app.a3f8c2.js).
Real example for an API endpoint:
Cache-Control: private, max-age=60
Per-user data, cached in the browser for 60 seconds but not by CDNs.
The Vary header
Vary tells caches to store separate versions of a response based on request headers.
Vary: Accept-Encoding, Authorization
This means: cache a different version for compressed vs. uncompressed responses, and per-auth-token. Without Vary: Authorization, a CDN might serve one user's private data to another user.
A CDN is serving an API endpoint that returns personalized user dashboard data. The response currently has the header Cache-Control: public, max-age=300. What is the most significant risk of this configuration?
3. Common Real-World Usage
CDN caching — A CDN like Cloudflare, Fastly, or CloudFront reads your Cache-Control headers and caches responses at edge nodes globally. A Cache-Control: public, max-age=300 on a product listing page means the CDN serves it for 5 minutes without touching your origin server.
Browser caching — Static assets (CSS, JS, images) are served with long max-age values (up to 1 year) combined with content hashes in filenames. When you deploy a new version, the filename changes, busting the cache.
API caching — Endpoints that return relatively stable data (product catalog, configuration, user profile) can be cached privately for short durations to reduce database load.
Conditional requests — Used for polling or large resources. A periodically checks GET /config and only processes the response if the ETag changed — saving bandwidth and processing.
Your team deploys a new CSS file but users are still seeing the old styles because their browsers cached the previous version. Which strategy correctly solves this problem while still allowing long-term browser caching for future deployments?
4. Trade-offs and Common Mistakes
Caching private data publicly
Returning Cache-Control: public on a user-specific response means CDNs will serve one user's data to every subsequent requester. Always use private for personalized responses.
Forgetting Vary: Accept-Encoding
If your server supports gzip compression, responses vary by encoding. Without this header, a CDN might cache the compressed version and serve it to a client that doesn't support gzip.
Setting too-long max-age without cache busting
If you cache main.js for 1 year without a hash in the filename, users won't get updates for a year. Always combine long max-age with content-addressed filenames for static assets.
Confusing no-cache and no-store
no-cachedoes NOT mean "don't cache" — it means "cache it but always revalidate."no-storemeans "don't store this anywhere."- Use
no-storefor sensitive responses (payment pages, authentication tokens).
Cache invalidation for stale data
caches are passive — they expire but aren't explicitly invalidated by your application by default. If you update a resource, clients with cached copies see stale data until expiry. Options:
- Use short
max-agefor frequently changing data - Use CDN cache-purge APIs (Cloudflare, Fastly) to actively invalidate on update
- Implement ETag/Last-Modified revalidation so clients always get fresh data when it changes
Not caching at all
Many teams skip caching entirely and rely on a fast database. This works at small scale but fails under load. Even a 10-second cache on a hot endpoint can absorb enormous traffic spikes.
A payment processing page returns sensitive authentication tokens in its response. A developer wants to ensure this data is never stored by browsers or intermediate caches. Which Cache-Control directive should they use, and why?
5. Interview Angle
How to explain it
" caching uses response headers to tell clients and intermediaries like CDNs how long they can reuse a response without hitting the server. There are two models: expiration (max-age) where the client trusts the cached copy for a duration, and validation (ETag/If-None-Match) where the client checks if the resource has changed. The Cache-Control header is the main tool. Getting caching right can dramatically reduce origin server load and improve response times."
Common interview questions
Q: What is the difference between no-cache and no-store?
no-cachesays "you can store the response, but you must revalidate with the server before serving it again."no-storesays "don't cache this at all — never store it on disk or in memory." Useno-storefor sensitive data like financial records.
Q: What is an ETag and how does it work?
An ETag is a server-generated fingerprint (usually a hash) of the resource content. The server sends it in the
ETagheader. On subsequent requests, the client sendsIf-None-Match: <etag>. If the content hasn't changed, the server returns 304 with no body. This saves bandwidth and lets clients cache aggressively while still getting fresh data when things change.
Q: Why do static asset URLs often include a hash like app.a3f8c2.js?
So we can set a very long
max-age(like 1 year) safely. When the content changes, the build system generates a new hash, which creates a new URL. The old cached version is effectively abandoned because no one will request that URL anymore. This is called cache busting.
Q: When would you use s-maxage?
When CDN caching should differ from browser caching. For example:
Cache-Control: public, max-age=0, s-maxage=600tells the browser not to cache (max-age=0 means stale immediately) but lets a CDN cache for 10 minutes. Note:privateands-maxageare contradictory per RFC 7234 —privateinstructs shared caches not to store the response, so combining them is incorrect. Usepublic, max-age=0to suppress browser caching while allowing CDN caching.
6. Practical Examples
Product catalog with CDN caching
HTTP/1.1 200 OK Content-Type: application/json Cache-Control: public, max-age=300, s-maxage=3600 ETag: "catalog-v42" Vary: Accept-Encoding [{ "id": 1, "name": "Widget", "price": 9.99 }, ...]
- Browsers cache for 5 minutes
- CDN caches for 1 hour
- Both support revalidation via ETag
User profile endpoint (private data)
HTTP/1.1 200 OK Content-Type: application/json Cache-Control: private, max-age=60 ETag: "user-99-v3" { "id": 99, "name": "Alice", "email": "alice@example.com" }
- Only the user's browser caches this
- CDN is excluded via
private - Revalidation supported via ETag
Sensitive endpoint — never cache
HTTP/1.1 200 OK Content-Type: application/json Cache-Control: no-store, no-cache { "access_token": "eyJhbGci..." }
Conditional request flow
Client → GET /feed (first request)
Server → 200 OK, ETag: "feed-v7"
Client → GET /feed, If-None-Match: "feed-v7" (second request)
Server → 304 Not Modified (if unchanged — no body transferred)
Server → 200 OK, ETag: "feed-v8" (if changed — new body + new ETag)
Glossary History
Click dotted jargon to save explanations here.
Glossary History
Click dotted jargon to save explanations here.