Caching missing or empty results to prevent repeated wasted lookups, while avoiding subtle correctness problems when the underlying data changes.
Negative caching stores absence as a reusable answer. Instead of caching only successful reads, the system also caches “not found,” empty result sets, or other safe negative outcomes for a short period. This can dramatically reduce repeated waste when callers keep asking for something that genuinely does not exist or is temporarily absent.
The danger is that absence can change quickly. A user may be created moments after a previous lookup failed. A product may become available right after a cached empty answer was stored. That means negative caches usually need shorter TTLs and much more careful semantics than positive caches.
sequenceDiagram
participant App
participant Cache
participant Store
App->>Cache: get(user:42)
Cache-->>App: negative hit (not found)
Note over App,Cache: repeated misses avoid origin load
Negative caching is valuable because repeated misses can be surprisingly expensive. Attack traffic, buggy retries, repeated lookups for deleted resources, or polling for not-yet-created data can all hammer the origin pointlessly. A short negative cache can protect the origin and smooth behavior under load.
Good candidates include:
Bad candidates often include:
Caching a 404 is not the same as caching a timeout or a permission failure.
This example stores a short-lived marker for a missing entity instead of repeatedly hitting the store.
1type NegativeEntry = { kind: "not-found" };
2
3async function getUserOrNull(userId: string): Promise<User | null> {
4 const key = `user:${userId}`;
5 const cached = await cache.get(key);
6
7 if (cached) {
8 const parsed = JSON.parse(cached) as User | NegativeEntry;
9 return "kind" in parsed ? null : parsed;
10 }
11
12 const user = await userStore.find(userId);
13 if (!user) {
14 await cache.set(key, JSON.stringify({ kind: "not-found" }), 15);
15 return null;
16 }
17
18 await cache.set(key, JSON.stringify(user), 300);
19 return user;
20}
What to notice:
The main risk is false absence. A negative cache entry may outlive the moment when the missing thing becomes available. That can produce confusing behavior:
This is why negative caches should usually be conservative and event-aware when possible.
Why should negative cache entries often have shorter TTLs than positive entries?
The stronger answer is that absence is frequently less stable than presence. The cost of a false negative can be high, so the system usually wants shorter reuse windows for “not found” answers than for established positive data.