Los universal links (iOS) y los App Links de Android abren tu app en lugar de tu sitio web cuando alguien toca un enlace https:// normal, y la parte del sitio web son dos archivos JSON que alojas tú mismo: apple-app-site-association para iOS y assetlinks.json para Android. Ambos viven en /.well-known/ en cada dominio que gestiona la app, los dos deben servirse por HTTPS sin redirecciones, y cada uno indica qué app puede abrir tus URL. La app también necesita su propia configuración (un derecho en iOS, un filtro de intents en Android), que se muestra más abajo junto a cada archivo.
Cómo funcionan los universal links y los App Links de Android
Los dos sistemas conectan un sitio web y una app en ambos sentidos: la app declara qué dominios gestiona, y el sitio web lo confirma. Apple describe los universal links como enlaces HTTP o HTTPS normales, de modo que una sola URL sirve para el sitio y para la app, y los usuarios sin la app reciben la página en el navegador (Apple). Los App Links de Android, en palabras de Google, se basan en el mismo sistema de intents que otros deep links, pero añaden un paso de verificación con un archivo de Digital Asset Links en tu sitio web (Android Developers).
| Universal links de iOS | App Links de Android | |
|---|---|---|
| Archivo en el sitio web | apple-app-site-association |
assetlinks.json |
| Ubicación | https://<dominio>/.well-known/apple-app-site-association |
https://<dominio>/.well-known/assetlinks.json |
| Lo que declara la app | Derecho Associated Domains, applinks:<dominio> |
Filtro de intents con android:autoVerify="true" |
| Identifica la app por | Team ID más bundle ID | Nombre del paquete más huella digital del certificado de firma |
| Cuándo se comprueba | En la instalación, a través del CDN de Apple | En la instalación, y en segundo plano en Android 15 y posteriores |

«Deep link» es el término más amplio. Un deep link normal puede usar un esquema personalizado o un enlace https:// no verificado; los universal links y los App Links son la variante verificada.
El archivo de iOS: apple-app-site-association
Crea un archivo llamado apple-app-site-association, sin extensión, y enumera los identificadores de tu app y los patrones de URL que la app debe abrir. Esta es la estructura de ejemplo de la documentación de Apple, abreviada:
{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.app"],
"components": [
{ "/": "/buy/*", "comment": "Open URLs that start with /buy/ in the app." },
{ "/": "/help/website/*", "exclude": true, "comment": "Keep these in the browser." }
]
}
]
}
}
El valor de appIDs es el prefijo del identificador de la aplicación (tu Team ID), un punto y el bundle identifier. Cada entrada de components coincide con una parte de la URL, y "exclude": true indica al sistema que no abra en la app las URL que coincidan (Apple, Supporting associated domains). Apple también acepta un formato anterior con appID y paths; su nota de depuración dice que no se mezclen los dos (TN3155).
Publícalo en https://<nombre de dominio completo>/.well-known/apple-app-site-association con un certificado válido y sin redirecciones. Tres reglas de las páginas de Apple suelen dar problemas:
- Cada subdominio necesita su propia entrada en la app. Un archivo en
example.comsirve paraapplinks:example.comyapplinks:*.example.com, mientras que un subdominio concreto comowww.example.comnecesita su propio archivo. - El archivo debe ser accesible desde cualquier dirección IP y para cualquier user agent. Un 403 o un 404 causado por un cortafuegos, un filtro de bots o un muro de inicio de sesión impide que los servidores de Apple lo guarden en caché.
- Desde iOS 14, los dispositivos no descargan el archivo de tu servidor. El CDN de Apple lo descarga y lo guarda en caché, los dispositivos buscan actualizaciones aproximadamente una vez por semana, y no hay forma de invalidar esa caché directamente; reinstalar la app descarga una copia más reciente (TN3155).
Durante el desarrollo, puedes añadir ?mode=developer a la entrada del derecho (applinks:example.com?mode=developer) para que el dispositivo lea el archivo directamente de tu servidor en lugar de hacerlo desde el CDN de Apple.
El archivo de Android: assetlinks.json
assetlinks.json es una matriz de declaraciones que nombran tu paquete y la huella SHA-256 del certificado que firma la app. Este es el ejemplo de Google para una app:
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example",
"sha256_cert_fingerprints":
["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
}
}]
Sustituye el nombre del paquete y la huella por los tuyos. Los requisitos, según la guía de Google (actualizada el 23 de octubre de 2026):
- Sírvelo en
https://<dominio>/.well-known/assetlinks.jsoncon el tipo de contenidoapplication/json. - Usa HTTPS aunque los filtros de intents de la app declaren también
http. - Sin redirecciones, ni 301 ni 302.
- Publícalo en cada dominio de host que gestiona la app.
- Un mismo archivo puede listar varias apps, y el campo
sha256_cert_fingerprintsadmite varias huellas, por ejemplo para las compilaciones de depuración y de producción.
Si usas Play App Signing, la huella que obtienes con keytool en tu equipo normalmente no coincidirá con la de los dispositivos de los usuarios. Google indica que tomes el fragmento JSON correcto de Play Console, en Release > Setup > App signing.
El lado de la app es un filtro de intents con autoVerify. Esta es la forma de la documentación de Android:
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="www.example.com" />
</intent-filter>
Android verifica cada nombre de host de esos filtros, y un host comodín como *.example.com necesita el archivo en el dominio raíz example.com (Android Developers). En Android 15 y posteriores, la verificación también se ejecuta periódicamente, y un cambio en el archivo puede tardar hasta siete días en propagarse; en Android 14 e inferiores, el archivo se lee solo al instalar o actualizar (Android Developers).
Lista de comprobación del servidor
Los dos archivos fallan por las mismas pocas razones, así que revisa el servidor una vez para ambos:
| Comprobación | Por qué | Cómo probarlo |
|---|---|---|
| HTTPS | Las dos plataformas lo exigen, y Apple pide un certificado válido | Abre la URL en un navegador |
| Sin 301 ni 302 | Las redirecciones no son compatibles con ninguno de los dos archivos | curl -sI devuelve un 200 |
| iOS: público para todas las IP y user agents | El CDN de Apple descarga el archivo, y un cortafuegos o un filtro de bots lo bloquea | curl -A "MyAgent-Bot/1.0" <url> devuelve el archivo |
Android: Content-Type: application/json |
Google lo indica como requisito | Cabecera de curl -sI |
| El mismo archivo en cada dominio y subdominio que listes | Cada host se verifica por separado | Ejecuta la comprobación por cada host |
Para el tipo de contenido, servir los dos archivos como application/json mantiene la configuración uniforme. La documentación de Apple no indica un tipo de contenido para el archivo de iOS, así que esa parte es una elección tuya. Un ejemplo estático para nginx:
location = /.well-known/apple-app-site-association {
default_type application/json;
}
location = /.well-known/assetlinks.json {
default_type application/json;
}
Cómo comprobar que funciona
Comprueba el archivo desde fuera y luego el comportamiento en un dispositivo. Los pasos de depuración de Apple, de TN3155:
- Ejecuta
curl -v https://<dominio>/.well-known/apple-app-site-associationy confirma un 200 y el JSON, no un 301, 302, 403 o 404. - En un Mac,
sudo swcutil dl -d <dominio>comprueba que el archivo se descarga, ysudo swcutil verify -d <dominio> -j <ruta-al-JSON> -u <URL>comprueba que una URL coincide con un patrón. - En un iPhone, pega el enlace en la app Notas y mantén pulsado. Si todo está configurado, aparecen las opciones de abrir en la app y de abrir en el navegador.
Para Android, la verificación manual de Google en Android 12 y posteriores (para una app que apunta a una versión anterior a Android 12, primero ejecuta adb shell am compat enable 175408749 com.example):
adb shell pm set-app-links --package com.example 0 all
adb shell pm verify-app-links --re-verify com.example
# espera unos minutos y luego:
adb shell pm get-app-links com.example
La salida enumera cada dominio con un estado como verified o legacy_failure. Google también ofrece un generador y comprobador de listas de declaraciones (herramienta de Digital Asset Links) para probar un archivo de declaraciones existente. Para una rutina de pruebas más amplia, consulta la lista de comprobación para pruebas de sitios móviles.
Cuándo los enlaces no abren la app
Escribir una URL en la barra de direcciones del navegador nunca abre la app en iOS, porque es navegación directa dentro del navegador; si el usuario ya está en tu dominio, un enlace al mismo dominio también se queda en Safari (TN3155). La solución de Apple para flujos como un botón de inicio de sesión es usar otro subdominio, con su propia entrada applinks y su propio archivo AASA, de modo que el sistema trate el toque como una salida del sitio.
Los navegadores integrados en las apps necesitan pruebas aparte. Apple indica que los enlaces tocados en una app de navegador o en un WKWebView pueden abrir tu app, pero también dice que los navegadores de terceros deciden si activan los universal links («Los universal links pueden no estar activados en todos los navegadores web», según TN3155), y que una app que abre tu enlace mediante su propia llamada open(_:) no abrirá el tuyo a cambio (Apple). Si el navegador de una app social concreta entrega el enlace a tu app no lo cubre la documentación de la plataforma, así que prueba cada una que te importe en un teléfono real.
Banner inteligente de la app como respaldo
Un banner inteligente de la app es un aviso solo para iOS en la página web: una etiqueta meta en el <head> de cada página donde lo quieras. Según Apple, abre la app si está instalada y enlaza con App Store si no lo está.
<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-argument=https://example.com/buy/42">
app-id es obligatorio, y el app-argument opcional pasa una URL a la app para que abra la pantalla correspondiente. Apple indica dos límites: los banners no aparecen dentro de un frame, y no aparecen en el simulador de iOS.
Si no publicas una app nativa, no necesitas ninguno de los dos archivos; una aplicación web progresiva se instala desde el navegador.
Comprueba la página de respaldo en tamaños de móvil
Todos los que no tienen la app, y cada enlace que no se transfiere, llegan a tu página web, así que esa página tiene que funcionar en un teléfono. Mobile View Extension recarga la pestaña con el user agent, el tamaño de pantalla y la relación de píxeles de un teléfono, así que ves la versión móvil real de la página de respaldo en varios tamaños, y puedes usar junto a ella la emulación de dispositivos de Chrome. No puede probar el traspaso a la app: se ejecuta en Chrome de escritorio, no tiene eventos táctiles ni apps instaladas, así que el manejo de enlaces necesita un iPhone o un teléfono Android, o el simulador de iOS o el emulador de Android. Para iOS sin hardware, consulta probar un sitio web en un iPhone sin tener un iPhone.
Preguntas frecuentes
¿Cuál es la diferencia entre universal links y deep links?
Los deep links son la categoría amplia; los universal links son la variante verificada de Apple, construida sobre enlaces HTTP o HTTPS estándar. Un deep link también puede usar un esquema de URL personalizado, que no está ligado a un sitio web que controles. Los App Links de Android son la forma verificada en Android, que añade la comprobación de Digital Asset Links a los deep links normales.
¿Dónde va apple-app-site-association?
Va en https://<dominio>/.well-known/apple-app-site-association, sin extensión de archivo, servido por HTTPS con un certificado válido y sin redirecciones. Cada subdominio que liste la app necesita su propia copia.
¿Por qué mi universal link abre Safari en lugar de la app?
Las causas habituales son una redirección, un 403 o 404 en el archivo, una entrada de subdominio que falta o un toque en el mismo dominio. Apple las enumera en TN3155; escribir la URL en la barra de direcciones o tocar un enlace al mismo dominio mientras se navega en Safari mantiene al usuario en el navegador. Comprueba antes el archivo con curl -v.
¿Necesito los dos archivos?
Necesitas el archivo de cada plataforma cuya app publiques. Un sitio con una app para iOS y otra para Android sirve los dos, y cada uno es independiente del otro.