Qué es un Service Worker y cuándo usarlo
Un Service Worker (SW) es un script que el navegador ejecuta en segundo plano, separado del hilo principal de la página. Funciona como un proxy de red programable: puede interceptar peticiones fetch, responder desde caché, actualizar recursos de forma inteligente y habilitar experiencias offline y de rendimiento percibido muy superiores.
Piensa en él como un portero con reglas claras: decide qué entra de la red, qué sale de tu caché y cuándo renovar el contenido. A diferencia de los Web Workers, los SW tienen ciclo de vida propio (no dependen de una pestaña concreta), persisten entre sesiones y disponen de APIs especiales como Cache Storage, Background Sync y Push.
Cuándo tiene sentido:
Tu web sirve muchos assets estáticos (CSS, JS, imágenes) y quieres reducir latencia.
Tienes rutas críticas (p. ej.,
/**) donde una copia offline evita pantallas en blanco.Deseas prefetch de recursos posteriores (posterior navegación) para que todo “parezca instantáneo”.
Quieres resiliencia ante redes inestables (móvil, zonas con mala cobertura).
Requisitos clave:
HTTPS (o
localhosten desarrollo).Ubica
sw.jsen una ruta cuyo scope cubra lo que necesitas (por ejemplo, en raíz/si quieres abarcar todo el sitio).Diseño “offline-first” en rutas HTML (fallback HTML + assets mínimos).
Diferencias con Web Workers y por qué actúa como “proxy de red”
Web Worker: acelera cálculos/CPU de tu página; no intercepta red ni vive fuera de la pestaña.
Service Worker: puede interceptar todas las peticiones de su scope, responder desde caché, sincronizar en segundo plano y recibir push. Es ideal para PWA.
Requisitos: HTTPS y localhost
En producción, sirve siempre bajo HTTPS y revisa cabeceras (por ejemplo,
Service-Worker-Allowedsi el archivo está en un subdirectorio y quieres ampliar el scope).En desarrollo, funciona en
http://localhost.
Ciclo de vida y actualización controlada
El ciclo de vida tiene tres grandes fases:
install: el navegador descarga el SW y ejecuta la lógica de instalación (típicamente precache).
waiting: si ya hay un SW activo, el nuevo queda “en espera” para no romper la sesión actual.
activate: el nuevo SW toma el control (limpieza de cachés antiguas, migraciones).
La gestión fina de actualizaciones evita sustos al usuario (assets desincronizados, SPAs a medio cargar). Dos APIs clave:
self.skipWaiting(): hace que el SW recién instalado pase de waiting a active de inmediato.clients.claim(): el SW activo toma control de todas las páginas bajo su scope sin esperar a que se recarguen.
Patrón recomendado (actualización con aviso):
Durante
install, precachea la nueva versión de assets (usa unCACHE_VERSION).No llames a
skipWaiting()todavía: anuncia que hay una versión nueva (postMessage al cliente).Muestra un banner en la UI (“Hay una nueva versión. Actualizar”).
Si el usuario acepta, envías un mensaje al SW para ejecutar
skipWaiting()y luego recargas las páginas controladas.
Ejemplo simple de ciclo:
skipWaiting() y clients.claim(): actualización sin sorpresas
Úsalos de forma coordinada con la UI para evitar “mezcla” de assets viejos y nuevos.
Si tu app es estática y el riesgo es bajo, puedes activar
skipWaiting()directamente tras precache (menor fricción).
Patrón “aviso de nueva versión” en la UI
En la página:
En el SW:
Registro y primer SW en 5 minutos
Registrar un SW es directo y reversible; si algo sale mal, lo desinstalas limpamente.
Dónde ubicar sw.js y definir el scope
En
/sw.jspara cubrir todo el sitio.Si lo pones en
/app/sw.js, por defecto solo controla/app/*. Puedes ampliar con la cabeceraService-Worker-Allowed: /.
navigator.serviceWorker.register() explicado (snippet incluido)
Checklist de verificación (errores comunes)
¿Estás en HTTPS (o
localhost)?¿
sw.jsdevuelve 200 y Content-Type: application/javascript?¿Scope correcto (raíz vs subcarpeta)?
¿Assets precache listados sin typos y accesibles?
¿Limpiando cachés antiguas en
activate?¿Sin cachear rutas sensibles (
/login,/api/auth)?¿Manejando fallback HTML para offline?
Intercepción de solicitudes y estrategias de caché
El evento fetch es el corazón del SW: decide de dónde responder y cuándo actualizar.
Cache Storage/IndexedDB: qué guardar y cómo invalidar
Cache Storage para respuestas HTTP (assets, HTML, JSON de lectura).
IndexedDB para datos estructurados, colas de sincronización, flags de versión.
Invalidación: versiona los nombres de caché (
runtime-v4) y limpia enactivate. Evita “cache busting” por URL con hash + strategia duplicada.
Patrones frecuentes (tabla de decisión)
| Estrategia | Ideal para | Ventajas | Riesgos / Notas |
|---|---|---|---|
| cache-first | Imágenes, fuentes, vendor JS/CSS con hash | Velocidad súper alta | Puede servir viejo si no se renueva |
| network-first | HTML y JSON donde quieres frescura | Datos actuales | Lento sin red; requiere fallback |
| stale-while-revalidate | Assets estáticos frecuentemente usados | Carga instantánea + actualización en background | Necesitas gestión de versiones |
| network-only | /api/auth, pagos, endpoints críticos | Seguridad / estado real | Sin beneficio offline |
| cache-only | Recursos inmutables (fuentes auto-hosteadas) | Cero latencia | Asegura política de actualización |
Casos de uso que sí aportan valor
Offline real (assets críticos + rutas HTML)
Precarga shell de la app (
index.html,app.js,styles.css) y un fallbackoffline.html.Para SPAs, cachea rutas de navegación (p. ej., usar URL Pattern o lógica por
Accept: text/html).
Prefetch predictivo y rendimiento percibido
Durante idle, el SW puede prefetchar rutas probables (siguiente página) para que la navegación sea instantánea.
Controla el peso: limita por dispositivo/red (usa
NetworkInformation.effectiveTypedesde la página para decidir).
API mocking y fallbacks elegantes
Sin red, responde con una copia reciente desde caché o una respuesta simulada (para listas).
Marca visualmente que es “modo sin conexión” e intenta revalidar en background con Background Sync.
Push Notifications y Background Sync (visión general)
Qué permite cada API y consideraciones de permisos
Push: recibir notificaciones desde el servidor (vía push service) incluso con la app cerrada. Requiere permiso explícito del usuario y estrategia de valor (mensajes útiles, no spam).
Background Sync: reintenta peticiones fallidas cuando vuelve la conectividad (subidas de formularios, colas). Ideal para UX robusta.
Cuándo integrarlo (y cuándo no)
Sí: apps de contenido relevante (actualizaciones útiles), mensajería, pedidos.
No: sitios informativos genéricos; si el valor es bajo, evitar pedir permiso (afecta confianza y SEO por métricas de abandono).
Seguridad, límites y gotchas
Qué NO puedes usar dentro del SW
Sin acceso al DOM, ni a
window, ni alocalStorage.Usa cache, fetch, clients, registration, IndexedDB y APIs de eventos.
Coste de arranque del SW y bypass estratégico
El SW añade un pequeño overhead en el arranque. No interceptes todo si no aporta valor. Por ejemplo, deja pasar ciertas rutas estáticas directamente a la red si tu CDN ya ofrece buena latencia.
Depuración y limpieza de cachés antiguas
Usa Application > Service Workers y Application > Cache Storage en DevTools para inspeccionar, forzar updates y borrar.
Implementa limpieza agresiva en
activatey registra métricas (cantidad de entradas, tamaño aproximado) si tu caso lo requiere.
FAQ y recursos para ir más lejos
Errores típicos en producción y cómo detectarlos
SW no se instala: revisa HTTPS, scope y tipo de contenido.
Assets 404 en precache: interrumpe
install→ nada se activa. Añade verificaciones y tests de build.Pantalla en blanco offline: falta fallback HTML o rutas no cubiertas.
Usuario ve mezcla de versiones: habilita actualización controlada con banner +
skipWaiting()bajo demanda.
Recursos oficiales y utilidades (MDN, Workbox, devtools)
Documentación de API (MDN) para detalles finos y compatibilidad.
Workbox (biblioteca de Google) ofrece estrategias de caché listas, routing declarativo y precache con manifest.
Conclusión
Un Service Worker bien diseñado es la pieza que convierte una web en app veloz, resiliente y usable sin conexión. La clave no es “cachearlo todo”, sino elegir la estrategia adecuada por tipo de recurso, cuidar el ciclo de vida y comunicar la actualización al usuario. Con el flujo de registro correcto, un precache mínimo y stale-while-revalidate para los assets adecuados, puedes reducir latencia, evitar errores por red y ofrecer una experiencia significativamente mejor.