Blog · Guías

El service worker en las notificaciones push: qué hace y cómo configurarlo

Qué hace el service worker cuando llega un push, dónde debe vivir, cómo añadirlo con una línea (también con Workbox), qué cabeceras de caché usar y cómo depurarlo.

Read in English

El service worker es la pieza que permite recibir notificaciones push con tu web cerrada: el navegador lo despierta cuando llega un mensaje, muestra la notificación y abre la página adecuada cuando el usuario la toca. No necesitas programar esa lógica, pero sí colocarlo bien: en tu dominio, por HTTPS, sin competir con otro worker y con cabeceras que dejen llegar las actualizaciones.

En resumen:

  • El service worker recibe los push aunque no haya ninguna pestaña abierta, muestra la notificación y gestiona el clic.
  • Debe servirse desde el mismo origen que tu web, por HTTPS y, salvo motivo, desde la raíz, porque su ámbito es la carpeta donde vive.
  • Basta un archivo /castly-sw.js de una línea, o esa línea al principio del service worker que ya tengas, también con Workbox.
  • Sírvelo con Cache-Control: no-cache y no cambies su URL entre despliegues.
  • Chrome, Firefox y Safari permiten inspeccionarlo; Chrome y Firefox, además, simular un push.

Qué hace el service worker en las notificaciones push

Un service worker es un script que el navegador ejecuta aparte de tus páginas, sin acceso al DOM: lo arranca cuando hay un evento que atender y lo detiene al quedar inactivo. Es justo lo que necesita el push, porque los mensajes no esperan a que el usuario tenga tu web abierta. El recorrido completo de un aviso lo contamos en qué es Web Push y cómo funciona.

En las notificaciones tiene tres tareas:

  1. Recibir el push. El navegador despierta al worker de tu web, aunque no haya ninguna pestaña abierta, y le pasa el contenido ya descifrado.
  2. Mostrar la notificación con el título, el texto, el icono y la imagen del aviso. Los navegadores exigen que cada push acabe en una notificación visible: si no aparece, Chrome enseña un aviso genérico y Safari puede retirar la suscripción.
  3. Gestionar el clic: cerrar la notificación y abrir la página del aviso, o enfocar la pestaña si ya estaba abierta.

Con castly no escribes esa lógica. El script que importas muestra la notificación, abre la url del aviso al hacer clic (reutilizando una pestaña de tu web si la hay) y confirma la entrega y el clic para que aparezcan en tus registros.

Dónde debe vivir: origen, HTTPS, raíz y ámbito

  • Mismo origen. El navegador solo registra service workers servidos desde el origen de la página. Por eso el archivo vive en tu web y, desde dentro, carga el código que necesita con importScripts().
  • HTTPS. Exige un contexto seguro. localhost vale para desarrollar; la IP de tu ordenador por HTTP, para probar desde el móvil, no: usa un túnel con HTTPS.
  • Ámbito. Por defecto, un service worker controla la carpeta donde está y todo lo que cuelga de ella: /castly-sw.js cubre todo el sitio, mientras que /assets/castly-sw.js se quedaría en /assets/. Ampliarlo desde una subcarpeta exige la cabecera Service-Worker-Allowed, como explica MDN en ServiceWorkerContainer.register(). Lo sencillo es la raíz.
  • Tipo MIME de JavaScript. Si tu hosting responde a cualquier ruta desconocida con el index.html de tu SPA, el registro falla.

Añadirlo con una línea, también con Workbox

Si no tienes service worker

Crea /castly-sw.js en la carpeta que tu proyecto sirve tal cual desde la raíz (public/ en Vite, Next.js o Nuxt) con esta línea:

importScripts('https://castly.sh/sdk/v1/sw.js');

No tienes que registrarlo: el SDK lo hace por ti cuando el usuario se suscribe. Tienes el resto de pasos en la guía de inicio rápido.

Si ya tienes uno

No añadas un segundo worker: pon la línea al principio del tuyo.

// sw.js
importScripts('https://castly.sh/sdk/v1/sw.js');

// …el resto de tu service worker (caché, Workbox, etc.)

Y dile al SDK dónde está:

Castly.init({ publicKey: 'pk_live_…', swPath: '/sw.js' });

swPath es la ruta del script y swScope, su ámbito. Pasa swScope solo si registras tu worker con un ámbito distinto de su carpeta, como /app/, y con el mismo valor que en tu registro, para que el SDK reutilice ese worker. El script importado solo atiende sus propios avisos, deja pasar el resto y no fuerza la activación ni toma el control de las páginas abiertas, así que no altera cómo se actualiza tu worker.

Con Workbox

Si escribes tu worker con Workbox (injectManifest), añade la línea al principio, como arriba. Si Workbox lo genera (generateSW, también desde plugins como vite-plugin-pwa), no edites el archivo, que se sobrescribe en cada build: usa su opción importScripts.

// workbox-config.js (o la opción workbox de tu plugin)
module.exports = {
  globDirectory: 'dist/',
  globPatterns: ['**/*.{html,js,css}'],
  swDest: 'dist/sw.js',
  importScripts: ['https://castly.sh/sdk/v1/sw.js'],
};

Ojo: importScripts() solo existe en los service workers clásicos. Si registras el tuyo con type: 'module', la línea fallará.

Cabeceras de caché para que las actualizaciones lleguen

Los navegadores buscan una versión nueva del service worker cuando el usuario navega por tu web, y al llegar un evento como un push si no lo han comprobado en las últimas 24 horas. Por defecto ignoran la caché HTTP para el archivo registrado, pero no la de tu CDN. Dos reglas:

  • Sin caché. Sirve el archivo con Cache-Control: no-cache, también en la CDN, para que cualquier cambio, como añadir la línea de importación, llegue en la siguiente visita.
  • Siempre la misma URL. Nada de sw.3f9a1c.js con hash: una página antigua en caché seguiría registrando la URL vieja y tendrías que actualizar el worker para poder actualizar el worker. web.dev lo explica en el ciclo de vida del service worker.

En Cloudflare Pages, Workers Static Assets o Netlify, un archivo _headers lo resuelve:

/castly-sw.js
  Cache-Control: no-cache

Depurarlo en Chrome, Firefox y Safari

  • Chrome y Edge. En DevTools, panel Application, sección Service workers, ves el script activo, su estado y su ámbito. Marca «Update on reload» mientras desarrollas. El campo Push dispara un evento push sin servidor, y Background services registra los push y las notificaciones.
  • Firefox. En about:debugging, apartado Este Firefox, encuentras tus service workers con botones para inspeccionarlos y para enviarles un push de prueba.
  • Safari. Activa las funciones para desarrolladores en Ajustes > Avanzado y elige tu web en Desarrollo > Service Workers. Para una web instalada en un iPhone, conecta el dispositivo al Mac y elígelo en ese menú. Safari no simula push.

Un matiz: el script importado ignora los mensajes que no tienen el formato de sus avisos, así que el push simulado de DevTools sirve para comprobar que el worker despierta, no para ver una notificación. Para una prueba completa, suscribe tu navegador desde el Playground del panel (pasa a ser dispositivo de prueba) y envíale un push, o envía a { "type": "test" } desde tu backend.

Errores comunes

  • La línea dentro de un evento. importScripts() va en el nivel superior del archivo: dentro de install o push falla.
  • Dos workers en la raíz. Tu PWA registra /sw.js y el SDK, /castly-sw.js, ambos con ámbito /: se sustituyen en cada visita. Únelos en uno y usa swPath.
  • Ruta equivocada. Si no hay archivo en swPath, Castly.subscribe() falla con el código service_worker; escúchalo con Castly.on('error', …).
  • «Unregister» o «Clear site data» en DevTools. Al eliminar el worker se elimina también la suscripción push de ese navegador: tendrás que volver a suscribirte.

Preguntas frecuentes

¿Puedo tener dos service workers en la misma web?

Puedes registrar varios con ámbitos distintos, como /app/ y /blog/, pero no dos para el mismo ámbito, porque el último sustituye al anterior. Si una librería trae su propio service worker, carga su código con importScripts() dentro del tuyo.

¿El service worker está siempre en ejecución?

No. El navegador lo arranca para atender un evento y lo detiene al quedar inactivo, a veces en segundos, así que lo que deba durar entre eventos va en IndexedDB o en la Cache API, no en variables globales.

¿Cómo se actualiza cuando cambio el código?

El navegador instala la versión nueva al detectar el cambio, pero esta espera a que se cierren las pestañas que usan la anterior, salvo que tu worker llame a self.skipWaiting().

¿Funciona el service worker en iPhone?

Sí, pero el push solo llega a webs añadidas a la pantalla de inicio desde iOS 16.4; en una pestaña de Safari no hay push. Castly.needsInstall() te dice cuándo tienes que guiar al usuario para instalarla, como contamos en web push en iPhone.

Ponlo en práctica con castlyNotificaciones push web y PWA para developers: un SDK de menos de 3 KB, una API REST y registros de entrega de cada dispositivo. Gratis hasta 10.000 notificaciones al mes.
Crear una cuenta gratis →

Envía tu primera notificación push hoy.

Crea un proyecto, añade el SDK y envía con una llamada a la API. Gratis para empezar.

Crear una cuenta gratis →