Service Worker: cómo un script deja tu web funcionar sin conexión

Service Worker: cómo un script deja tu web funcionar sin conexión

@programacion

Lo esencial

  • Un service worker corre en un hilo aparte del hilo principal y no tiene acceso al DOM.
  • Se registra con navigator.serviceWorker.register() y exige HTTPS, salvo en localhost.
  • El evento fetch intercepta cada petición de la página y decide si responde desde caché o red.
  • La Cache API guarda pares completos de Request y Response, no solo texto como localStorage.
  • El evento install descarga y cachea recursos; el evento activate borra versiones viejas de caché.
  • Cachear mal datos que cambian seguido (precios, stock) genera información obsoleta sin ningún error visible.
  • Chrome DevTools, pestaña Application, sección Service Workers, muestra el estado y fuerza la actualización.

Qué es un service worker

Cerrá la pestaña, desconectá el wifi y la página sigue respondiendo: eso es un service worker haciendo su trabajo entre tu navegador y la red.

Es un script de JavaScript que el navegador ejecuta en un hilo separado del hilo principal, sin acceso al DOM ni a las variables globales de la página. Actúa como un proxy programable: cada petición HTTP que hace tu app puede pasar primero por él. La referencia técnica completa está en la documentación de MDN.

El navegador lo mantiene vivo incluso después de cerrar la pestaña, aunque puede apagarlo por inactividad para ahorrar memoria y despertarlo de nuevo cuando llega un evento, como una petición fetch o una notificación push.

Cómo se registra

Para activarlo hay que registrarlo desde el script principal de la página. El navegador exige HTTPS, con la única excepción de localhost durante desarrollo.

if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registro => console.log('Service worker registrado en', registro.scope))
.catch(error => console.error('Error al registrar', error));
}

La ruta '/sw.js' define también el scope: a qué páginas controla. Un archivo en la raíz del sitio controla todo el dominio; uno ubicado en /app/sw.js solo controla las rutas que empiezan con /app/.

El ciclo de vida: instalación y activación

Un service worker pasa por tres estados: instalación, activación y ejecución. El evento install se dispara una sola vez, cuando el navegador descarga el script por primera vez o detecta que cambió un solo byte del archivo.

const CACHE_NAME = 'tienda-v3';
const RECURSOS = ['/', '/estilos.css', '/catalogo.js', '/logo.png'];

self.addEventListener('install', evento => {
evento.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(RECURSOS))
);
});

waitUntil() le indica al navegador que no marque la instalación como completa hasta que la promesa se resuelva. Si cache.addAll() falla porque un recurso no existe, la instalación entera falla y el service worker no llega a activarse.

El evento activate llega después, cuando el service worker anterior ya soltó el control. Es el momento de borrar cachés de versiones viejas, algo que evita acumular megabytes de recursos obsoletos en el navegador del usuario.

self.addEventListener('activate', evento => {
evento.waitUntil(
caches.keys().then(nombres =>
Promise.all(
nombres
.filter(nombre => nombre !== CACHE_NAME)
.map(nombre => caches.delete(nombre))
)
)
);
});

Cómo intercepta peticiones el evento fetch

Cada vez que la página pide una imagen, un script o llama a una API, el navegador dispara el evento fetch dentro del service worker antes de que la petición llegue a la red. respondWith() decide qué respuesta recibe la página.

self.addEventListener('fetch', evento => {
evento.respondWith(
caches.match(evento.request).then(respuestaCache => {
return respuestaCache || fetch(evento.request);
})
);
});

Esta estrategia se llama cache-first: si el recurso ya está en caché, lo devuelve sin tocar la red. Funciona bien para assets estáticos que casi no cambian (CSS, imágenes, fuentes), pero es mala idea para datos que sí cambian seguido, como el stock de un producto.

Cache API: no es lo mismo que localStorage

La Cache API guarda pares completos de objetos Request y Response, con headers y body incluidos, no solo strings. caches.open('tienda-v3') crea o abre un almacén con ese nombre; caches.match() busca ahí una respuesta previa que coincida con la petición.

A diferencia de localStorage, que es síncrono y limitado a unos 5 MB de texto, la Cache API es asíncrona (basada en promesas) y puede guardar megabytes de respuestas binarias, como imágenes o archivos de fuentes.

Tres estrategias de caché según el tipo de recurso

No hay una estrategia única correcta: depende de qué tan seguido cambia el recurso que estás sirviendo.

  • Cache-first: responde desde caché si existe y solo va a la red si falta. Ideal para assets estáticos que casi no cambian.
  • Network-first: intenta la red primero y cae a caché si falla. Sirve para contenido que necesita estar actualizado, como precios o disponibilidad.
  • Stale-while-revalidate: responde al instante con lo cacheado y en paralelo actualiza la caché para la próxima visita.

Notificaciones push y sincronización en segundo plano

El mismo service worker que cachea recursos puede recibir notificaciones mientras la pestaña está cerrada, mediante el evento push, y reintentar peticiones fallidas cuando vuelve la conexión con la Background Sync API.

Ambas capacidades dependen de que el navegador mantenga registrado el service worker en segundo plano; no son exclusivas de una app instalada como PWA.

Cuándo un service worker no conviene

Cachear mal una API que cambia seguido (precios, stock, saldo de una cuenta) genera datos obsoletos silenciosos: el usuario ve algo que ya no es cierto y no hay ningún error visible. Network-first, o directamente no cachear esa ruta, evita el problema.

Depurar un service worker roto también cuesta más que depurar JavaScript normal, porque queda registrado en el navegador incluso después de cambiar el código: hay que forzar la actualización desde DevTools o versionar el nombre de la caché, como tienda-v3 en el ejemplo, para invalidar la anterior.

Conclusión

Un service worker no reemplaza al backend, pero decide qué pasa cuando la red falla, tarda o directamente no existe. Registrarlo, cachear los recursos correctos y elegir la estrategia según el tipo de dato es lo que separa una web que muere sin wifi de una que sigue respondiendo.

📖 Versión extendida con más detalle: https://elsolitario.org/2026/09/17/service-workers-web-sin-conexion/?utm_source=telegraph&utm_medium=instant_view&utm_campaign=programacion

Report Page