All Docs
FeaturesDepositClearUpdated March 12, 2026

tRPC Cache-Control Policy

tRPC Cache-Control Policy

As of v0.1.178, all tRPC API responses include explicit Cache-Control headers. The behaviour is determined by a dedicated cache policy module and is applied automatically at the route handler level — no changes to individual procedures are required.

Overview

The policy classifies every tRPC request into one of three tiers and sets the appropriate Cache-Control header before the response is sent.

POST / mutation  →  no-store
GET (sensitive)  →  no-store
GET (static)     →  public, s-maxage=60, stale-while-revalidate=300
GET (default)    →  private, no-cache

Tiers in Detail

Tier 1 — Never Cached (Mutations)

Applies to: All POST requests (i.e. any tRPC mutation).

Cache-Control: no-store

Mutations change server state and must never be served from a cache. This tier is unconditional.


Tier 2 — Never Cached (Sensitive GETs)

Applies to: GET requests where the procedure involves personally identifiable information (PII), secrets, or per-user state.

Cache-Control: no-store

Examples of procedures that fall into this tier include anything touching user profiles, authentication tokens, deposit case data, or tenant-specific records.


Tier 3 — Public CDN Cache (Static GETs)

Applies to: GET requests for fully static, globally shared reference data.

Cache-Control: public, s-maxage=60, stale-while-revalidate=300
ParameterValueMeaning
s-maxage60 sCDN edge caches the response for 60 seconds.
stale-while-revalidate300 sCDN may serve a stale response for up to 5 minutes while revalidating in the background.

Procedures currently in this tier:

  • Plans
  • Wear-and-tear categories
  • Compliance help content
  • Inventory templates
  • Agent roles

Because these procedures return identical data for all organisations, it is safe for a CDN edge node to cache and serve them to multiple callers.


Tier 4 — Private Browser Cache (Default)

Applies to: All other GET requests (org-scoped data that is not explicitly sensitive).

Cache-Control: private, no-cache

The private directive ensures the response is stored only in the requesting browser's cache and never at a shared CDN edge. no-cache means the browser must revalidate with the server before using a stored response.


Batch Request Handling

tRPC supports batching multiple procedure calls into a single HTTP request. The policy uses taint propagation to ensure safety: if any procedure in a batch would be classified as no-store, the entire batch response receives no-store.

This means a batch containing one sensitive procedure and several static procedures will always be no-store — the most restrictive policy wins.


Data Isolation Guarantee

Org-scoped data is always private and never reaches the CDN edge. Only globally identical static reference data can be publicly cached. Tenant data isolation is fully preserved.


Implementation

The policy is implemented in two files:

FilePurpose
src/lib/trpc/cache-policy.tsClassifies requests and returns the correct Cache-Control header string.
src/app/api/trpc/[trpc]/route.tstRPC route handler — calls the policy module and attaches the header to every response.

The module is fully unit-tested (tests/lib/trpc/cache-policy.test.ts, 19 tests) covering URL parsing, all policy branches, and batch taint propagation.


Adding a Procedure to the Static Tier

To mark a new procedure as publicly CDN-cacheable, it must meet all of the following criteria:

  1. Returns identical data regardless of which organisation or user calls it.
  2. Contains no PII, secrets, or user-specific state.
  3. Is a query (not a mutation).

If all criteria are met, add the procedure name to the static allowlist in src/lib/trpc/cache-policy.ts. Ensure a unit test is added to tests/lib/trpc/cache-policy.test.ts to cover the new entry.