Cache

Eurobase can answer a request for a Workers app from a stored copy of an earlier response, without running your Worker. Your app decides what is stored with its response headers. Nothing is stored unless it asks.

Cached responses

A cached answer starts no Worker and uses no Worker CPU. It still counts as a request. A stored copy is used until its lifetime ends, for at most 30 days, or until you purge it. Each Eurobase server that runs your app keeps its own copies, so a request served by another server can miss once.

What is stored

A GET response with a status such as 200, 301 or 404 is stored when its headers give it a lifetime for shared caches:

  • Cache-Control: public, s-maxage=60: stored for 60 seconds. Browsers ignore s-maxage.
  • CDN-Cache-Control: max-age=60: stored for 60 seconds, whatever Cache-Control tells browsers.
  • Cache-Control: max-age=60: not stored. max-age alone is for browsers.
  • Cache-Control: no-store: not stored, nor with private or no-cache.

Never stored or answered from the cache:

  • Requests with an Authorization header, or with a cookie your app set.
  • Responses that set a cookie, or that send Vary: *, Vary: Cookie or Vary: Authorization.
  • Event streams, WebSockets, and bodies over 10 MiB.

Each URL, query string included, is its own copy. If a response changes with a request header, such as Accept-Language, name it in Vary; otherwise the first visitor's version is served to everyone. Nuxt cached handlers don't turn their varies option into a Vary header, so send that header yourself. stale-while-revalidate serves the old copy while a new one renders, and stale-if-error serves it when your Worker fails, unless the response sends must-revalidate.

Next.js

Next.js middleware runs inside your Worker, so a cached answer would skip it. Next.js responses are therefore stored only when your app sends CDN-Cache-Control itself, and every other request runs your middleware. Your pages and data are still cached: use cache, fetch caching and ISR pages are stored on Eurobase, and revalidateTag and revalidatePath work as in Next.js.

Checking a response

A request that can be cached gets a Cache-Status response header:

  • eurobase; hit; ttl=42: answered from the cache, with 42 seconds of lifetime left. A negative value means a stale copy was served.
  • eurobase; fwd=uri-miss; stored: your Worker answered, and the response was stored.
  • eurobase; fwd=uri-miss: your Worker answered, and the response wasn't stored.
  • eurobase; fwd=stale; fwd-status=503; ttl=-5: your Worker failed, and a stale copy was served instead.
curl -s -o /dev/null -D - https://<project>.eurobase.dev/ | grep -i cache-status

Purging

Tag a response with Cache-Tag: products, home. Next.js tags from cacheTag count too. You can purge by tag, by path or everything: from the CLI, from an AI agent with the eurobase_purge_cache tool, or with the API: POST /commands/v1/projects/{projectId}/cache/purge.

npx --yes eurobase@0.3.6 cache purge --project prj_example --tags products
npx --yes eurobase@0.3.6 cache purge --project prj_example --paths /blog,/pricing
npx --yes eurobase@0.3.6 cache purge --project prj_example --everything

Each server applies a purge on your project's next request there, and purged pages are rendered again. Purges and your app's revalidateTag and revalidatePath calls share a limit of 10 a second per project, with bursts of 100.

New deployments

Copies belong to the deployment that rendered them. A new deployment or a rollback starts with an empty cache, so visitors never get pages from an earlier version.