Skip to main content

This page covers the cache option: what Cache-Control an object is served with, where to set it, and which value to pick. Every SDK takes the same values.

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#

ObjectPathcacheHow readers see a change
Unique path per upload (unique paths)new each time'immutable'the path is new, nothing to invalidate
Stable path that changes rarelystable'immutable' plus the versioned URLthe URL changes with the etag
Fixed URL you do not control, or a client that drops query stringsstable'revalidate'a 304 check on every read
Private or sensitiveany'no-store', or a short durationthe link expires; see below

The cache option#

ValueStored header
'immutable'public, max-age=31536000, immutable
'revalidate'public, max-age=0, must-revalidate
'no-store'no-store
a duration ('15m', 3600)public, max-age=<seconds>
unsetpublic, max-age=3600
anything containing = or ,stored exactly as written

A duration is converted to whole seconds, so '1500ms' stores max-age=1. The grammar is on Types.

The raw header#

Anything containing = 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#

cache: 'revalidate'cache: '60s'
Unchanged object304, no bodyfull object, once a minute
Object just overwrittennext read sees itup to 60 s of the old bytes

'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#

The default for every object this client stores. Client options: TypeScript, Python, Go.

On a put#

The JSON update 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#

Signed into the URL and handed back in the returned headers, so the uploader has to send it verbatim.

On a direct browser upload#

Decided per upload in the before-upload callback on your server and signed into the presigned PUT. See Authorizing an upload.


Private buckets#

cachePublic bucketPrivate bucket
unsetpublic, max-age=3600private, max-age=3600
'1m'public, max-age=60private, max-age=60
'immutable'public, max-age=31536000, immutableprivate, max-age=31536000, immutable
'revalidate'public, max-age=0, must-revalidateprivate, max-age=0, must-revalidate
'no-store'no-storeno-store

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#

The versioned URL is the URL with the etag on the query, so it changes whenever the content does. A stable path stored immutable and served through the versioned URL 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.

A private bucket has neither URL.


no-store and signed reads#

These are two separate mechanisms. The link expires at its returned expiry, 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 Signed read URLs for link lifetimes.


What the upload route itself caches#

An upload route’s GET serves its constraints document with a 60 second Cache-Control of its own, unrelated to the objects the route stores. See Constraints.