Caching
Turbulence caches the HTML it serves so that the second-and-later visitors to a page get a fast response without re-running the whole transformation pipeline. Understanding how the cache works will help you predict when a change to your site will be visible to visitors, and how to force a refresh if you need to.
The Short Version
Section titled “The Short Version”Each unique URL is cached independently. When you add a domain through the dashboard, the default cache TTL is 1 hour (3600 seconds) — configurable per domain under Settings. After that window, the cache is considered stale. Stale entries are kept for up to 7 days (the max-stale age) while Turbulence revalidates in the background. When an entry expires entirely, the next visitor triggers a fresh fetch from your origin.
If no per-domain TTL has been synced yet, the worker falls back to 15 minutes (900 seconds).
If you need to invalidate a page right now — for example, you just published a critical fix — Turbulence supports a few ways to do that, listed below.
What’s Cached
Section titled “What’s Cached”Turbulence caches the transformed HTML response for each unique URL. The full URL (including the path and the query string) is the cache key. A request to /about and a request to /about?utm_source=twitter are treated as two distinct pages and cached separately.
The cache lives in Cloudflare R2, the same storage your self-hosted fonts and static assets are stored in. The transformed HTML is served from the edge CDN after that, so the response is fast for visitors regardless of where they are.
What’s Not Cached
Section titled “What’s Not Cached”- Non-HTML responses (CSS, JS, images, JSON, anything with a non-HTML
Content-Type) are not cached by Turbulence. They pass straight through. The HTML is cached; the assets referenced from the HTML are served as-is by your origin (or by PicPerf, in the case of optimized images). - Cache-bypass parameters. If a request includes
?pp_verify=1, Turbulence skips the cache for that one request. The?bust=1parameter also bypasses the cache, but only in the worker’s development environment. More on this in Verifying It Works. - POST requests and other non-GET methods are not cached.
How Freshness Works
Section titled “How Freshness Works”Each cached entry has two lifetimes associated with it:
- Time to live (TTL). How long the cached version is considered “fresh” — the period during which Turbulence returns it without checking with your origin. Dashboard default: 3600 seconds (1 hour). Worker fallback: 900 seconds (15 minutes).
- Max stale age. The longest Turbulence will keep serving a stale entry while it revalidates in the background. After this, the next request triggers a full re-fetch. Default: 604800 seconds (7 days). This is not currently configurable from the dashboard.
While the cache is fresh, every request gets the cached version instantly.
Once the TTL is up but you’re still within the max-stale window, the behavior is:
- The visitor gets the cached version immediately. Their response is just as fast as a fresh-cache hit.
- In the background, Turbulence revalidates the entry by fetching from your origin and running the transformation pipeline again.
- The next visitor (and every one after that, until the entry re-stales) gets the freshly-revalidated version.
This is called stale-while-revalidate, and it’s the default behavior. It’s the right tradeoff for almost every site: visitors get fast responses, your origin doesn’t have to handle every request, and updates to your pages are picked up once the configured TTL elapses (default: 1 hour) and background revalidation runs.
After the max-stale age is up, the entry is considered expired. The next request after that goes to your origin, and the freshly-transformed HTML is written to the cache to replace it.
How Cache Lifetime Is Determined
Section titled “How Cache Lifetime Is Determined”Per-domain cache TTL lives in PicPerf’s KV store and can be configured from the dashboard under each domain’s Settings tab. Valid range: 60 seconds to 86400 seconds (24 hours). Default: 3600 (1 hour).
The max-stale age (7 days) is set at the worker level and is not exposed in the dashboard. If you’re not sure what TTL to use, the default is fine for most sites. Lower it if you publish frequently and need faster cache turnover; raise it if your content is mostly static.
The Cache-Control Header
Section titled “The Cache-Control Header”Every HTML response from Turbulence carries this Cache-Control header:
Cache-Control: public, max-age=3600, stale-while-revalidate=86400That tells the visitor’s browser and any intermediate CDN to cache the response for one hour, and to use a stale version for up to a day while revalidating in the background. This is separate from the server-side cache at Turbulence — it’s the browser’s cache, not the edge’s. So your visitors get a fast response even on a cold edge cache, as long as they’ve visited recently.
Response Headers You’ll See
Section titled “Response Headers You’ll See”When Turbulence serves a request, the response includes a few headers that tell you what happened. They’re useful for verifying things are working and for debugging:
X-PicPerf-Cache— one ofHIT(served from the edge cache, fresh),STALE(served from the edge cache, but revalidating in the background), orMISS(no cache entry, fetched from origin and transformed just now).X-PicPerf-Cache-Age— the age of the cached entry in seconds, when applicable.X-PicPerf-Pipeline-Stage—fullif all transformations completed inline, orpartialif some (the slow ones: font self-hosting, static asset upload) are still finishing in the background. The response is still complete and functional in either case.X-PicPerf-Optimized—trueif at least one transformation step actually changed the HTML.falsemeans the pipeline ran but produced identical output (rare, but possible on already-optimized pages).X-PicPerf-Steps— a comma-separated list of the steps that modified the HTML, likeimages,image-sizing,resource-hints,scripts. Useful for confirming which optimizations are firing.
Forcing a Refresh
Section titled “Forcing a Refresh”There are a few ways to get Turbulence to fetch a page from your origin instead of serving a cached copy:
- Wait for the cache to go stale. Once your configured TTL elapses (default: 1 hour), Turbulence starts background revalidation. Visitors still get the cached version immediately while the refresh happens behind the scenes.
- Lower the cache TTL in Settings. If you need faster turnover going forward, reduce the TTL on your domain’s Settings page. This affects new cache cycles, not entries already stored.
- Append
?pp_verify=1to a URL. This bypasses the cache for that single request and runs the full pipeline synchronously. It’s primarily meant for development and debugging — see Verifying It Works for details. Note: this does not write the fresh result to the cache, so it’s a way to check the pipeline without affecting what other visitors see.
There is currently no per-URL or per-domain cache purge button in the dashboard. If you need an immediate cache invalidation across your site, reach out to support.
What Happens If Your Origin Is Down
Section titled “What Happens If Your Origin Is Down”If Turbulence tries to revalidate and your origin is unreachable, the cached version keeps being served (within the max-stale window). Visitors see your site as if nothing happened. After the max-stale age expires, the next request will fail until your origin is back, but the failure is graceful — Turbulence will return a 502 with a clear error, not corrupt HTML.
This is one of the big benefits of the cache: it provides a buffer against brief origin outages.