Cache-Control / cache TTL
The header that tells browsers and CDNs how long they may keep a copy before checking with you again.
See it
What it is
Cache-Control is the response header where you state the rules, and the TTL (time to live) is the number inside it. The pieces you actually use: max-age (seconds a browser may keep it), s-maxage (seconds a shared cache like a CDN may keep it, and it wins over max-age), public vs private (may a shared cache store this at all), immutable (a promise that the body will not change while the response is still fresh, so a client can skip revalidating during that window), and stale-while-revalidate (serve the old copy instantly, refresh in the background).
The two-tier default that works for most sites: content-hashed assets get 'public, max-age=31536000, immutable' because a new build means a new filename, and HTML or API responses get something like 'public, max-age=0, s-maxage=60, stale-while-revalidate=86400' so browsers always check while the CDN absorbs the load.
Gotcha: no-cache does not mean do not cache. It means store it, but revalidate with the origin before every reuse. The directive you want for genuinely secret responses is no-store. Second gotcha: a long max-age is irrevocable. Once a browser has it, no purge reaches that laptop, so never put a year-long TTL on a URL whose contents can change. Third: immutable is not a TTL and it is not 'never revalidate'. It only says the representation will not change during the freshness lifetime that max-age already granted, so it never creates or extends that lifetime, and without a long max-age beside it, it buys you nothing.
Ask AI for it
Set caching headers for this app in two tiers. On content-hashed static assets (JS, CSS, fonts, images with a hash in the filename) send 'Cache-Control: public, max-age=31536000, immutable'. On HTML and cacheable API responses send 'Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=86400' plus an ETag. On anything user-specific or authenticated send 'Cache-Control: private, no-store'. Add the Vary: Accept-Encoding header, and show me the final header value per route in a table.