Blog · iOS y Safari
Cómo hacer tu web instalable como PWA: el manifest paso a paso
Qué pide cada navegador para instalar tu web, un manifest mínimo explicado campo a campo, iconos maskable y por qué el service worker sigue siendo clave para las push.
Para que un navegador ofrezca instalar tu web como PWA necesitas servirla por HTTPS y enlazar un manifest con nombre, iconos de 192 y 512 píxeles, start_url y un modo display como standalone. Chrome ya no exige un service worker para instalar, aunque lo necesitarás igualmente si quieres enviar notificaciones push. Aquí construimos ese manifest campo a campo y vemos qué hace cada navegador con él.
En resumen:
- Chrome y Edge piden HTTPS y un manifest con
nameoshort_name, iconos de 192 y 512 px,start_urlydisplay.- Safari en Mac (desde macOS Sonoma) y en iPhone permite añadir cualquier web al Dock o a la pantalla de inicio; el manifest decide nombre, iconos y cómo se abre.
- Firefox de escritorio no instala PWAs a partir del manifest.
- Separa los iconos
anyde losmaskabley fija unidestable desde el primer día.- El service worker no es requisito para instalar, pero sí para las push; en iPhone, además, la web tiene que estar en la pantalla de inicio.
Qué pide cada navegador para instalar tu web
No hay un único criterio de «instalable»; cada motor decide cuándo ofrece la instalación:
| Navegador | Cómo se instala | Qué exige |
|---|---|---|
| Chrome y Edge (escritorio y Android) | Icono en la barra de direcciones, menú o tu propio botón | HTTPS, manifest con los campos mínimos y algo de interacción: al menos un clic y unos 30 segundos en la página |
| Safari en macOS Sonoma o posterior | Archivo › Añadir al Dock | Nada: funciona con cualquier web, tenga manifest o no |
| Safari en iPhone y iPad | Compartir › Añadir a pantalla de inicio | Nada; desde iOS 26 la web se abre como app por defecto aunque no tenga manifest |
| Firefox de escritorio | No instala PWAs desde el manifest | — |
| Firefox en Android | Acceso directo en la pantalla de inicio | La web se abre dentro del navegador |
En Android, solo Chrome (con servicios de Google) y Samsung Internet hacen una instalación completa; el resto crea un acceso directo. La guía de MDN sobre cómo hacer instalable una PWA mantiene estos datos al día. Conclusión: el manifest es obligatorio para el aviso de instalación de Chromium y, en Apple, es lo que pone tu nombre, tus iconos y tus colores en lugar de un acceso genérico.
Un manifest mínimo que funciona
Crea /manifest.webmanifest (también vale manifest.json; el nombre da igual) y sírvelo con el tipo application/manifest+json:
{
"id": "/",
"name": "Reservas Lumen",
"short_name": "Lumen",
"description": "Reserva y gestiona tus citas",
"start_url": "/app?source=pwa",
"scope": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#1f6feb",
"lang": "es",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
Enlázalo desde el <head> de todas las páginas, no solo de la portada:
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#1f6feb">
Qué aporta cada campo:
nameyshort_name: el primero sale en el diálogo de instalación; el segundo, bajo el icono. Como regla práctica, dejashort_nameen unos 12 caracteres.start_url: la página que se abre al lanzar la app. Con?source=pwaseparas en tu analítica las visitas desde la app instalada.display:standalonequita la interfaz del navegador yminimal-uideja unos controles básicos. Conbrowser, Chrome no la considera instalable.background_colorytheme_color: colores de la pantalla de arranque y de la barra de título; usa los de tu CSS para evitar un destello.descriptionyscreenshots: opcionales; Chrome los usa para un diálogo de instalación más completo.
Iconos: tamaños, maskable y Apple
Chromium exige dos tamaños: 192×192 y 512×512, en PNG. Android recorta los iconos con la forma que elige cada fabricante (círculo, cuadrado redondeado…), y ahí entra el campo purpose:
any(valor por defecto): el icono se muestra tal cual. Úsalo para el logo con fondo transparente.maskable: el sistema puede recortarlo. Lo importante debe caber en un círculo centrado con un radio del 40 % del ancho, porque los bordes pueden desaparecer.
Evita declarar "purpose": "any maskable" en un mismo archivo: o el logo queda diminuto donde no se recorta, o pierde los bordes donde sí. Mejor dos archivos, como en el ejemplo anterior.
En iPhone y iPad, Safari usa los iconos del manifest desde iOS 15.4, pero si tu HTML incluye <link rel="apple-touch-icon">, ese icono tiene prioridad. Si lo heredaste de una versión antigua de la web, comprueba que no muestre un logo desactualizado. Lo habitual es un PNG cuadrado de 180×180 sin transparencias, ya que iOS rellena el fondo transparente de negro.
start_url, scope e id: evita sorpresas
Tres campos definen qué es «tu app» para el navegador, y los errores con ellos aparecen meses después, cuando ya hay usuarios con la app instalada.
scope: hasta dónde llega la app
Si no declaras scope, se toma la carpeta de start_url. Con "start_url": "/app/inicio" y sin scope, el ámbito queda en /app/, y un enlace a /precios se abrirá fuera de la ventana de la app o con la barra del navegador. Si tu app y tu web comparten dominio y quieres que todo se vea integrado, declara "scope": "/". Ten en cuenta que start_url debe quedar dentro del ámbito; si no, el navegador descarta tu scope.
id: la identidad de la app
Si falta id, el navegador usa start_url como identidad. Eso significa que cambiar start_url más adelante, por ejemplo para añadir ?source=pwa, puede convertirla en «otra» app para quien ya la tenía instalada. Declara un id fijo, como "/", antes de publicar y no lo toques. iOS también lo usa para distinguir instalaciones y sincronizar los ajustes de Concentración entre dispositivos.
Service worker: no hace falta para instalar, sí para las push
Durante años Chrome exigió un service worker con manejador fetch para considerar instalable una web. Ya no: hoy le basta el manifest. Pero el service worker sigue siendo la pieza que recibe las notificaciones push con la web cerrada, así que si quieres avisar a tus usuarios lo necesitarás de todos modos. Si nunca has escrito uno, empieza por tu primer service worker para push.
En iPhone y iPad hay un requisito extra: las push web solo funcionan cuando la web está en la pantalla de inicio (iOS 16.4 o posterior). En iOS, instalar no es un extra, es la condición para poder pedir permiso, y como Safari no ofrece la instalación por sí solo, tienes que explicarla tú. Lo detallamos en web push en iPhone. Con el SDK de castly puedes detectar ese caso y mostrar tus propias instrucciones:
<script src="https://castly.sh/sdk/v1/castly.js"></script>
<script>
Castly.init({ publicKey: 'pk_live_…' });
if (Castly.needsInstall()) {
// iPhone o iPad en Safari sin instalar: enseña "Compartir › Añadir a pantalla de inicio"
document.querySelector('#instalar-ios').hidden = false;
} else if (Castly.isSupported()) {
document.querySelector('#activar-avisos').onclick = () => Castly.subscribe();
}
</script>
Cómo comprobar que tu web es instalable
- DevTools de Chrome › Application › Manifest: muestra los campos que ha leído, previsualiza los iconos (también recortados como maskable) y lista los motivos por los que la web no es instalable.
- Instalación real: prueba en Chrome de escritorio y Android, y en Safari con Añadir al Dock o a la pantalla de inicio. Revisa nombre, icono y que
start_urlabre la página correcta. - Modo de visualización:
window.matchMedia('(display-mode: standalone)').matcheste dice si se está ejecutando como app, y en iOS tambiénnavigator.standalone.
No busques la puntuación PWA en Lighthouse: esa categoría se retiró en la versión 12, en 2024. Los criterios de instalación de Chrome en web.dev son la referencia actual.
Para tener tu propio botón «Instalar app» en Chromium, guarda el evento beforeinstallprompt y llama a prompt() desde un clic. No es estándar y Safari no lo implementa: trátalo como una mejora, no como el único camino:
let aviso;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
aviso = e;
botonInstalar.hidden = false;
});
botonInstalar.addEventListener('click', async () => {
aviso.prompt();
const { outcome } = await aviso.userChoice; // 'accepted' o 'dismissed'
botonInstalar.hidden = true;
});
Preguntas frecuentes
¿Necesito un service worker para que mi web sea instalable?
No en Chrome, Edge ni Safari: basta con HTTPS y un manifest válido, y Safari ni siquiera exige el manifest. Sí lo necesitas para funcionar sin conexión y para recibir notificaciones push con la web cerrada.
¿Puedo tener dos PWAs en el mismo dominio?
Sí, si cada una tiene su propio manifest con un id distinto y ámbitos que no se pisen, por ejemplo /app/ y /admin/. Cada página debe enlazar el manifest de la app a la que pertenece.
¿Por qué Chrome no muestra la opción de instalar mi web?
Suele faltar un campo obligatorio o fallar un icono, o bien display vale browser, la app ya está instalada o aún no hay suficiente interacción con la página. La sección Manifest de DevTools te dice cuál es.
¿Se puede instalar una PWA en Firefox de escritorio?
No a partir del manifest: Firefox de escritorio no ofrece instalar PWAs como Chrome. Tu web funciona igual en una pestaña, y allí las push no dependen de instalarla.
¿Hace falta instalar la web para enviar notificaciones push?
Solo en iPhone y iPad, donde la web debe estar en la pantalla de inicio. En Chrome, Edge, Firefox y Safari de escritorio, y en Android, el usuario puede suscribirse desde una pestaña normal.