---
title: "Service Workers: Cache-First vs Network-First - ¿Cuál Usar y Por Qué?"
excerpt: "Descubre las estrategias de caching en Service Workers y aprende cuándo usar cache-first, network-first o stale-while-revalidate para optimizar tu Progressive Web App."
date: "2026-02-13T10:00:00.000Z"
category: "JavaScript"
seo_title: "Service Workers: Cache-First vs Network-First en PWA"
seo_description: "Comparativa de las 5 estrategias de caching en Service Workers: Cache-First, Network-First, Stale-While-Revalidate, Network-Only y Cache-Only. Código funcional y tabla de uso por tipo de recurso."
author:
  name: "angel cruz"
  picture: "https://angelcruzdevcdn.nyc3.cdn.digitaloceanspaces.com/images/me/angel-cruz.png"
---

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.

```javascript
// 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é.

```javascript
// 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.

```javascript
// 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.

```javascript
// 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.

```javascript
// 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:

```javascript
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:

```javascript
// 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](https://developer.chrome.com/docs/workbox/caching-strategies-overview) - Documentación oficial de Google
- [Service Worker Caching and HTTP Caching](https://web.dev/articles/service-worker-caching-and-http-caching) - Artículo técnico de web.dev
- [Offline-First PWAs: Service Worker Caching Strategies](https://www.magicbell.com/blog/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.

---

## Sitemap

Índice completo del sitio: [/sitemap.md](https://angelcruz.dev/sitemap.md)

Canónico HTML: [https://angelcruz.dev/post/service-workers-estrategias-caching-guia-practica](https://angelcruz.dev/post/service-workers-estrategias-caching-guia-practica)
