Los Service Workers son uno de los pilares de las Progressive Web Apps (PWA), permitiendo que tus aplicaciones web funcionen offline, carguen más rápido y ofrezcan una experiencia similar a las apps nativas. Pero… ¿cómo decides qué contenido cachear y cuándo servirlo? Ahí es donde entran las estrategias de caching.
Elegir la estrategia incorrecta puede resultar en:
- Usuarios viendo contenido desactualizado (aunque tengan internet)
- Carga lenta innecesaria
- Experiencia offline rota
En este artículo te voy a explicar las principales estrategias, cuándo usar cada una, y te mostraré código real que puedes implementar hoy mismo.
Las 5 estrategias fundamentales
1. Cache-First (Cache, Fallback Network)
¿Cómo funciona? El Service Worker busca primero en el caché. Si encuentra el recurso, lo sirve inmediatamente. Si no está cacheado, va a la red.
// Estrategia Cache-First
async function cacheFirst(request, cacheName) {
// Buscar en caché primero
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse; // Rápido: sirve de caché
}
// Si no está en caché, ir a la red
try {
const networkResponse = await fetch(request);
if (networkResponse && networkResponse.status === 200) {
// Cachear para próximas visitas
const cache = await caches.open(cacheName);
cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
console.error('Fetch failed:', error);
throw error;
}
}
¿Cuándo usarla?
- Assets estáticos (JS, CSS, fonts, imágenes)
- Recursos con versionado (ej:
app.v2.min.js) - Contenido que raramente cambia
Ventajas:
- Velocidad máxima (carga instantánea de caché)
- Funciona offline para contenido visitado
Desventajas:
- Puede servir contenido desactualizado
- Requiere estrategia de invalidación de caché
2. Network-First (Network, Fallback Cache)
¿Cómo funciona? Siempre intenta ir a la red primero. Solo si falla (usuario offline), recurre al caché.
// Estrategia Network-First
async function networkFirst(request, cacheName) {
try {
// Intentar red primero
const networkResponse = await fetch(request);
if (networkResponse && networkResponse.status === 200) {
// Actualizar caché con contenido fresco
const cache = await caches.open(cacheName);
cache.put(request, networkResponse.clone());
}
return networkResponse; // Contenido fresco
} catch (error) {
// Si falla la red, buscar en caché
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse; // Fallback offline
}
throw error; // Sin red ni caché
}
}
¿Cuándo usarla?
- Páginas HTML (contenido principal)
- APIs con datos dinámicos
- Contenido que debe estar actualizado
Ventajas:
- Siempre sirve contenido fresco cuando hay conexión
- Fallback offline para páginas ya visitadas
Desventajas:
- Más lento que cache-first (espera red primero)
- Consume datos aunque el contenido esté cacheado
3. Stale-While-Revalidate
¿Cómo funciona? Sirve de caché inmediatamente (incluso si está desactualizado), pero actualiza en segundo plano para la próxima visita.
// Estrategia Stale-While-Revalidate
async function staleWhileRevalidate(request, cacheName) {
const cache = await caches.open(cacheName);
// Buscar en caché
const cachedResponse = await caches.match(request);
// Actualizar en segundo plano (no esperar)
const fetchPromise = fetch(request).then((networkResponse) => {
if (networkResponse && networkResponse.status === 200) {
cache.put(request, networkResponse.clone());
}
return networkResponse;
});
// Servir caché inmediatamente si existe
return cachedResponse || fetchPromise;
}
¿Cuándo usarla?
- Avatares de usuario
- Imágenes de productos
- Contenido que puede estar “un poco desactualizado”
Ventajas:
- Carga instantánea (usa caché)
- Se auto-actualiza en segundo plano
- Funciona offline
Desventajas:
- Usuario puede ver contenido desactualizado temporalmente
- Consume ancho de banda en cada visita (actualización background)
4. Network-Only
¿Cómo funciona? Siempre va a la red, nunca usa caché. Es como no tener Service Worker para ese recurso.
// Estrategia Network-Only
async function networkOnly(request) {
return fetch(request); // Directo a la red
}
¿Cuándo usarla?
- Requests POST/PUT/DELETE (no cachear mutaciones)
- Datos extremadamente sensibles al tiempo
- APIs de terceros sin control
5. Cache-Only
¿Cómo funciona? Solo sirve de caché, nunca va a la red. Útil para precaching durante instalación del SW.
// Estrategia Cache-Only
async function cacheOnly(request) {
return caches.match(request);
}
¿Cuándo usarla?
- Assets precargados durante instalación
- Recursos offline-first
Implementación práctica: Service Worker completo
Aquí te dejo un Service Worker funcional que usa diferentes estrategias según el tipo de contenido:
const CACHE_VERSION = 'v1';
const STATIC_CACHE = `static-${CACHE_VERSION}`;
const DYNAMIC_CACHE = `dynamic-${CACHE_VERSION}`;
const IMAGE_CACHE = `images-${CACHE_VERSION}`;
// Precachear assets críticos
const PRECACHE_ASSETS = [
'/',
'/app.css',
'/app.js',
];
// Instalación: precachear
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(STATIC_CACHE).then((cache) => {
return cache.addAll(PRECACHE_ASSETS);
})
);
self.skipWaiting();
});
// Activación: limpiar cachés viejos
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== STATIC_CACHE
&& name !== DYNAMIC_CACHE
&& name !== IMAGE_CACHE)
.map((name) => caches.delete(name))
);
})
);
return self.clients.claim();
});
// Fetch: aplicar estrategias según tipo de recurso
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Solo GET requests
if (request.method !== 'GET') return;
// Assets estáticos: Cache-First
if (isStaticAsset(url)) {
event.respondWith(cacheFirst(request, STATIC_CACHE));
}
// Imágenes: Stale-While-Revalidate
else if (isImage(url)) {
event.respondWith(staleWhileRevalidate(request, IMAGE_CACHE));
}
// Páginas HTML: Network-First
else if (isNavigationRequest(request)) {
event.respondWith(networkFirst(request, DYNAMIC_CACHE));
}
});
// Helpers
function isStaticAsset(url) {
return url.pathname.match(/\.(js|css|woff2?|ttf)$/);
}
function isImage(url) {
return url.pathname.match(/\.(jpg|jpeg|png|gif|webp|avif|svg)$/);
}
function isNavigationRequest(request) {
return request.mode === 'navigate';
}
Caso real: ¿Artículo nuevo en tu blog?
Esta fue la pregunta que inspiró este artículo: ¿qué pasa cuando publicas contenido nuevo?
Con network-first para páginas HTML:
// Usuario visita /blog/articulo-nuevo
fetch('/blog/articulo-nuevo')
↓
// 1. SW intenta la RED primero
if (usuario_online) {
// Descarga artículo nuevo
// Lo cachea para futuras visitas
// Usuario ve contenido FRESCO
} else {
// 2. Usuario offline
// Red falla
// No está en caché (nunca visitó)
// Error nativo del navegador
}
Resultado: Artículos nuevos siempre se descargan frescos. El caché solo funciona como fallback offline para contenido ya visitado.
Tabla comparativa rápida
| Estrategia | Velocidad | Contenido Fresco | Offline | Mejor para |
|---|---|---|---|---|
| Cache-First | Muy alta | No | Sí | JS, CSS, fonts |
| Network-First | Media | Sí | Sí* | HTML, APIs |
| Stale-While-Revalidate | Alta | Parcial | Sí | Imágenes, avatares |
| Network-Only | Media | Sí | No | POST/PUT/DELETE |
| Cache-Only | Muy alta | No | Sí | Precached assets |
*Solo funciona offline para contenido previamente visitado.
Preguntas Frecuentes
¿Puedo combinar varias estrategias en un mismo Service Worker?
Sí, de hecho es la mejor práctica. Usa cache-first para assets estáticos, network-first para HTML, y stale-while-revalidate para imágenes.
¿Cómo actualizo el caché cuando cambio mi código?
Cambia el CACHE_VERSION en tu Service Worker. El evento activate limpiará automáticamente cachés viejos.
¿Qué pasa si el usuario nunca visitó una página y está offline?
Con network-first, si la página no está cacheada y no hay conexión, el navegador mostrará su error nativo de “No hay conexión”.
¿Service Workers consumen mucho espacio?
No necesariamente. Puedes limitar el tamaño de caché o usar estrategias de expiración. Workbox (de Google) tiene helpers para esto.
¿Funciona en todos los navegadores?
Service Workers son soportados por Chrome, Firefox, Safari, Edge. IE11 no los soporta (pero ya está deprecado).
¿Cómo debugging un Service Worker?
Chrome DevTools → Application → Service Workers. Ahí puedes ver el SW activo, desregistrarlo, y simular offline.
Recursos adicionales
Si quieres profundizar más, te recomiendo:
- Workbox - Caching Strategies Overview - Documentación oficial de Google
- Service Worker Caching and HTTP Caching - Artículo técnico de web.dev
- Offline-First PWAs: Service Worker Caching Strategies - Guía práctica PWA
Conclusión
Las estrategias de caching en Service Workers son la clave para construir aplicaciones web rápidas, resilientes y que funcionen offline. No existe una “mejor estrategia” universal - todo depende del tipo de contenido:
- Assets estáticos → Cache-First (velocidad)
- Contenido dinámico → Network-First (frescura)
- Imágenes/Avatares → Stale-While-Revalidate (balance)
Con la implementación correcta, puedes ofrecer experiencias que rivalicen con apps nativas, manteniendo tu código simple y mantenible. ¿Ya implementaste Service Workers en tu proyecto? Cuéntame en los comentarios qué estrategia te funcionó mejor.

