Tenant-Aware Caching Without Data Leaks

A shared cache can return another customer’s data when the tenant identifier is missing from a key or an HTTP response varies by a tenant header the cache ignores. Treat cache scope as part of the authorization boundary.

Last updated: September 26, 2026.

function projectCacheKey(int $tenantId, int $projectId, int $version): string
{
    return "tenant:{$tenantId}:project:{$projectId}:v{$version}";
}

$key = projectCacheKey($sessionTenantId, $projectId, $projectVersion);
$project = $cache->get($key);

if ($project === null) {
    $project = $repository->findForTenant($sessionTenantId, $projectId);
    $cache->set($key, $project, 300);
}

The tenant ID is derived from verified server-side context, and the database lookup remains tenant-scoped. A version component makes invalidation explicit after changes.

Define every cache dimension

Include tenant, resource, locale, permission-sensitive representation, and relevant version in the key. User-specific results also need a user or role dimension. Do not trust an unverified request header merely because it is convenient for a reverse proxy.

Microsoft’s multitenant API guidance explicitly warns that tenant-varying data needs the tenant identifier in its cache key.

Invalidate without cross-tenant scans

Use predictable tenant prefixes and version tokens. On update, delete the exact resource key or increment a tenant-scoped generation. Avoid global flushes, and avoid wildcard deletion on the request path. When offboarding, a controlled background job can scan only the tenant prefix.

Protect availability and privacy

  • Set per-tenant size or item limits where possible.
  • Avoid caching secrets or highly sensitive data unless the cache is designed for it.
  • Encrypt transport and restrict cache credentials and networks.
  • Log misses and latency by tenant without logging cached payloads.
  • Test two tenants with identical resource IDs and different values.

Per-tenant quotas help prevent a noisy neighbor. The underlying repository must still apply tenant-isolated queries because cache misses reach the database.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov