Caching Strategies
6 strategies, patterns, reactive, timeout, cache eviction.
Swoff provides 6 caching strategies that run in the Service Worker. Every fetch event is intercepted, matched against your configured patterns, and handled by the appropriate strategy — no manual route registration needed.
This guide is purely conceptual. You don't need to import any swoff API for caching strategies to work. Everything here is configured in
swoff.config.json. Strategies activate automatically once the Service Worker is registered viainitServiceWorker(). No code changes required — works the same in React, PHP/Laravel, HTMX, or vanilla HTML.
For the internal mechanics of strategy resolution, serveFromCache, the fallback chain, the reactive entry lifecycle, and navigation mode rules, see the Caching System architecture.
Preconditions
- Service worker registered and controlling the page (see Service Worker guide)
Status
Already on by default. After swoff init, the generated SW applies these default patterns:
| Pattern | Strategy | Behavior |
|---|---|---|
/api/* | network-first | Try network, fall back to cache |
/static/* | cache-first | Serve from cache on hit, fetch from network on miss |
The 6 strategies
{
"features": {
"serviceWorker": {
"strategy": {
"default": "cache-first",
"patterns": {
"/api/*": "network-first",
"/static/*": "cache-first"
},
"timeout": 10,
"reactive": {
"defaults": {
"staleTime": 0,
"refetchInterval": 0,
"refetchOnReconnect": false,
"refetchOnFocus": false
}
}
}
}
}
}| Strategy | How it decides | Best for |
|---|---|---|
cache-first | Cache hit → serve from cache. Cache miss → fetch and cache. No background refresh. | Static assets (JS, CSS, images) |
network-first | Fetch from network first. Network fails or times out → serve from cache. Cache miss → fallback. | API responses that need freshness |
stale-while-revalidate | Cache hit (fresh or stale) → serve from cache + always trigger background refresh. Cache miss → fetch normally. | Content that can serve slightly stale data |
cache-only | Cache hit → serve from cache. Never fetches from network. Cache miss → fallback. | Immutable assets, precached content |
network-only | Bypasses SW entirely (early return). Browser handles the request directly. Never cached. | Non-cacheable requests, analytics beacons |
reactive | Cache hit + fresh (within staleTime) → serve. Cache hit + stale → serve + background refresh. Cache miss → fetch and cache. Refreshes only when past staleTime. | Dashboard data, polling, focus/refetch patterns |
Pattern matching
URL patterns use glob syntax:
| Pattern | Matches |
|---|---|
/api/* | /api/notes, /api/users (single segment) |
/api/** | /api/notes/123, /api/users/456/posts (any depth) |
/static/*.js | /static/app.js, /static/vendor.js |
!/api/auth/** | Negation — excludes auth routes |
Patterns can be a strategy name string or an object with per-route overrides:
{
"patterns": {
"/api/notes": {
"strategy": "reactive",
"staleTime": 30,
"refetchInterval": 60,
"refetchOnFocus": true
},
"/api/search/*": {
"strategy": "network-first",
"timeout": 5
}
}
}Request-level overrides
Any HTTP client (native fetch, Axios, HTMX, PHP, curl) can override strategy behavior at call time by sending these headers:
X-SW-Strategy: stale-while-revalidate
X-SW-Stale-Time: 30
X-SW-Refetch-Interval: 60// Browser — native fetch
fetch("/api/notes", {
headers: { "X-SW-Strategy": "reactive" },
});
// HTMX — via hx-headers
<button hx-get="/api/notes" hx-headers='{"X-SW-Strategy": "network-first"}'>
Load Notes
</button>;Available headers: X-SW-Strategy, X-SW-Stale-Time, X-SW-Refetch-Interval, X-SW-Refetch-On-Focus, X-SW-Refetch-On-Reconnect. The refetch* and staleTime options only apply to the reactive strategy — other strategies ignore them.
These headers are consumed by the SW fetch handler and automatically stripped before the request is forwarded to the network. No external server ever sees
X-SW-*headers — safe to use with any third-party or cross-origin API.
Config
{
"features": {
"serviceWorker": {
"strategy": {
"default": "cache-first",
"timeout": 10,
"maxRuntimeCacheAge": 2592000,
"normalizeKey": false,
"ignoreQueryParams": [],
"patterns": {},
"reactive": {
"defaults": {
"staleTime": 0,
"refetchInterval": 0,
"refetchOnReconnect": false,
"refetchOnFocus": false
}
}
}
}
}
}strategy.default— fallback strategy when no pattern matches or header override is present. One of:"cache-first","network-first","stale-while-revalidate","cache-only","network-only","reactive".strategy.timeout— fetch timeout in seconds applied to all SW-initiated network requests. Default:10. If the fetch doesn't respond within this time, the strategy falls back (e.g.,network-firstreturns cached content).strategy.maxRuntimeCacheAge— maximum age in seconds for runtime-cached entries. Default:2592000(30 days). Older entries are pruned during SW activation.strategy.normalizeKey— sort query parameters alphabetically before hashing the cache key. Default:false.strategy.ignoreQueryParams— array of query parameter names to strip from the cache key (e.g.["_rsc"]for Next.js RSC payloads).strategy.patterns— URL glob pattern → strategy config or strategy name. When the value is an object (per-route override):strategy— the strategy to applytimeout— per-route fetch timeout override (inheritsstrategy.timeout)staleTime— seconds before a cached response is considered stale (reactive only).0means every request after the first triggers a background refresh.refetchInterval— poll interval in seconds (reactive only)refetchOnFocus— refetch when tab regains focus (reactive only)refetchOnReconnect— refetch when network returns (reactive only)
strategy.reactive.staleTime— default:0.strategy.reactive.refetchInterval— default:0(no polling).strategy.reactive.refetchOnReconnect— default:false.strategy.reactive.refetchOnFocus— default:false.
Related
- Caching System architecture — strategy resolution flow, reactive entry lifecycle, navigation
serveFromCacheand fallback chain, HTML cache isolation - Library Comparison — Swoff vs Workbox / Serwist / vite-plugin-pwa / next-pwa