cache option: what Cache-Control an object is served with, where to set it, and which value to pick.
Cache-Control is written once, at upload, and stored with the object. The CDN and the browser honor it on every read. There is no per-request override; changing it means writing the object again.
Which value to pick
The cache option
'1500ms' stores max-age=1. The grammar is on Types.
The raw header
= or , is treated as a raw header and stored as written. Use this for s-maxage, stale-while-revalidate, no-transform and anything else the three keywords do not cover.
revalidate versus a short max-age
'revalidate' stores public, max-age=0, must-revalidate. The cached copy is checked with If-None-Match on every read, so an unchanged object costs a 304 with no body. A short max-age serves stale bytes until it expires, then re-downloads the whole object.
'revalidate' costs a round trip per read, but is never stale and never downloads the bytes twice.
Where you can set it
Four places. The most specific one wins.On the bucket
lib/blob.ts
On a put
updateJson takes it too, for the object it rewrites. So do copy and move, for the destination. Without it the source’s value carries over.
On a signed upload URL
headers, so the uploader has to send it verbatim.
On a direct browser upload
lib/uploads.ts
Private buckets
On a private bucket,
private replaces public, so no shared cache keeps a copy of an object only a signed request may read. This follows the bucket’s visibility in the console; nothing in the code declares it.
A raw header string is passed through as written, visibility included: cache: 'public, max-age=60' on a private bucket stores public, max-age=60.
Immutable plus a versioned URL
app/api/avatar/route.ts
versionedUrl is url with the etag on the query, so it changes whenever the content does. A stable path stored immutable and served through versionedUrl is cached for a year, and every overwrite produces a URL no cache has seen. The path never moves, so nothing has to be deleted.
url and versionedUrl are both undefined on a private bucket.
no-store and signed reads
expiresAt, but the stored Cache-Control outlives it: with a long max-age the reader’s browser keeps the bytes after the link stops working. If a reader must not keep the bytes, store the object with no-store.
no-store drops the visibility scope entirely and stores no-store on public and private buckets alike.
See signedReadUrl for link lifetimes.
What the upload route itself caches
An upload route’sGET serves its constraints document with a 60 second Cache-Control of its own, unrelated to the objects the route stores. See Constraints.