Cache invalidation

Make cache keys describe the result they contain

Include every input that changes a cached response. A resource identifier alone is rarely enough for tenant data, translations or filtered views.

In this article

Write the response function first

Consider a product summary that changes by tenant, price list, language and product revision. If the cache key contains only the product identifier, the first request determines what later callers receive.

List the inputs that affect the returned value before building a key. Separate identity from presentation. A shared raw product record may be safe to cache once, while a rendered price card needs customer-specific scope.

Avoid caching an entire authorised response merely because its database lookup is expensive. It may be simpler to cache safe source data and perform access checks and presentation for each request.

Encode scope without ambiguity

Use a structured key builder with explicit components. Do not concatenate arbitrary user input with an unescaped separator if different inputs could produce the same string.

JSON example
{
  "namespace": "product-summary-v2",
  "tenant": "tenant-example-a",
  "product": "product-example-19",
  "priceList": "trade-example",
  "language": "en-AU"
}

This example describes the logical key, not a required storage format. The implementation may encode or hash the canonical representation. Hashing hides neither the need for correct scope nor the sensitivity of the stored value.

Keep secrets and raw personal data out of key names. Keys often appear in operational tools and logs even when payload inspection is restricted.

Define the value and expiry together

Store enough metadata to understand the copy, such as source revision and load time. Choose expiry according to the permitted age of that result. Do not extend a value's life indefinitely merely because it is read often.

For a failed lookup, distinguish a confirmed absence from a temporary backend error. Caching a timeout as not found can turn a brief outage into a persistent false result.

Use a namespace version when changing the stored shape so incompatible application releases do not interpret each other's entries. Plan memory usage while both namespaces exist.

Verify collisions and lifecycle changes

Create two tenants with the same local product identifier, two price lists and two languages. Request them in different orders and confirm that each result is correct.

Then change a price, remove access and deploy the previous application version. These cases reveal whether freshness, authorisation and format compatibility depend on assumptions hidden in the key builder.

Keep the key construction in shared code with a clear owner. A second implementation in a background job can otherwise populate entries that the main application reads under a different interpretation.

Primary sources

OWASP: multitenant security

References checked 11 September 2026.