You edit an article, open the site, and still see its old title. The database has the change. Where did the old answer come from?
A page request can encounter several stored copies before it reaches that database. Caching makes repeated reads faster, but it also gives us more places to understand when a result is out of date.
Let's follow an article page through those layers and decide which copies can safely outlive a change.
The browser can reuse a response
A browser may store an HTTP response and reuse it according to its cache rules. A fresh response can save a network request; an older stored response may need validation before reuse.
For an illustrative public response, the server might send:
Cache-Control: public, max-age=60
ETag: "article-17-v3"
The freshness period permits reuse for a limited time. The validator can help check whether a stored representation still matches the current one when validation is required. These are different jobs.
A long-lived immutable asset URL can be useful for versioned CSS or images. A frequently edited article title needs a policy that matches how soon readers should see changes. Don't give both the same lifetime just because both use HTTP.
An edge cache shares eligible responses
A CDN can serve cached assets or configured public responses from an edge location. That can reduce origin traffic and the distance some requests travel.
The exact location and routing aren't ours to infer from a marketing map. Measure the real request path. Also distinguish a browser cache from a shared edge cache: a response suitable for one user may be unsafe to reuse for another.
Some reverse proxies or load-balancing products provide caching too, when configured. Load distribution alone isn't a cache. Check which component actually stores responses and how its key and policy are defined.

The application can keep a useful computed answer
Suppose rendering the article's sidebar requires several queries for related posts and category counts. The service can cache that computed result instead of rebuilding it on every request.
A local in-memory cache serves one process. A shared cache such as Redis can make a result available to several instances. That changes both access and failure behaviour: a local copy disappears with its process, while a shared service adds a network dependency.
Include the relevant inputs in the cache identity. A sidebar for one language, publication state or access context must not accidentally replace another. Store only results that are appropriate to share under that key.
Here's a simplified path; any configured cache hit can avoid work farther down it:
This is one possible arrangement, not a requirement to cache at every layer. Each copy needs its own identity, freshness and access policy.
I am the one who caches.
A title edit needs an invalidation plan
Now change the title. The database commits it, but the application result, edge response and browser response may still contain the old value.
We can expire copies after a defined time, update or invalidate them when the article changes, or change a versioned identity so new readers request the new result. Different layers can use different strategies.
Which stored copy are we willing to show, and for how long after the source changes?
TTL expiry bounds age under its policy, but doesn't promise immediate freshness. Invalidation needs reliable propagation and sensible race handling. Draw the write path as carefully as the read path, then test the edit from another browser and another application instance.
A cache miss can become a rush
When a popular entry expires, many requests may discover the miss together and rebuild the same expensive answer. The database suddenly sees load the cache usually hid.
Depending on the feature, coordinate one rebuild, refresh ahead of expiry, or serve a permitted stale result while refreshing. Spread expiry times where synchronized misses would be harmful. Choose the policy around freshness and failure requirements instead of applying all of these mechanisms automatically.
Monitor hit rate alongside request latency, rebuild cost and origin load. A high hit rate can still hide a small number of painfully expensive misses.
Logs and indexes have different responsibilities
Databases use memory buffers to reuse data pages, while CPUs and operating systems have their own caches. Application code doesn't necessarily control those as a neat sequence of lookup steps.
A write-ahead log records changes for durability and recovery. A broker's retained messages preserve work or history. They aren't ordinary response caches that we can discard whenever another copy exists. PostgreSQL's WAL introduction explains the durability role.
Search indexes and materialized views are derived data structures with their own maintenance and query behaviour. They can avoid repeated work, but describing every stored copy as "a cache" loses the recovery requirements that make it useful.
Know how to find the answer's age
For our edited article, identify which layer served the response, inspect its cache headers, and compare the application result with the database. That is a better debugging path than clearing unrelated caches until the title changes.
Caching works well when we can explain the stored result's identity, access, freshness and rebuild path. Start with the expensive work you actually want to avoid. Then test an edit and a cache failure, because both reveal what the fast read normally hides.
Try it yourself
The article title changed in the database, but the site serves the old title. Why isn't clearing only Redis a reliable fix?
Share this article with your friends
Know someone who would enjoy this? Send it their way.