11 min de lecturaFunciones

Deep links sin SDK: Universal Links y App Links

Abre tu app desde un enlace con dos archivos JSON y sin SDK. Cómo funcionan Universal Links y App Links con un dominio de enlaces cortos, y sus límites.

Marius Voß
DevRel · edge infra

Un deep link es una URL que el sistema operativo entrega a una app en lugar de a un navegador. Púlsala con la app instalada y la app se abre. Sin la app, el navegador carga la página web. En iOS esto se llama Universal Links, en Android App Links, y ninguno necesita un SDK.

La configuración son dos archivos JSON en un dominio que controles, más un entitlement o un filtro de intents en la app. A continuación: ambos archivos, cómo encaja un dominio de enlaces cortos, la parte que la mayoría de las guías omiten (la app recibe la URL corta, no el destino de la redirección) y los tres trabajos que un SDK sigue haciendo mejor.

Esto importa más que antes. Google cerró Firebase Dynamic Links el 25 de agosto de 2025, y sus preguntas frecuentes ahora devuelven HTTP 404 para esos enlaces (preguntas frecuentes de soporte de Firebase, consultadas el 2026-10-24). Esas preguntas frecuentes citan un proveedor para la paridad completa de funciones, y App Links más Universal Links para apps que solo necesitan abrirse cuando están instaladas. Para el caso de la impresión a la instalación, un código QR para descargas de apps pone el mismo enrutamiento detrás de un código escaneable.

Resumen

  • Android App Links (Android 6, API 23, y posteriores) y iOS Universal Links cubren "abrir en la app si está instalada, página web si no" sin SDK de terceros.
  • Cada uno necesita un dominio que controles, servido por HTTPS, con un archivo de asociación bajo /.well-known/.
  • En un dominio de enlaces cortos, la app se lanza con la URL corta. El código de tu app debe resolverla a una pantalla. La 302 nunca se ejecuta.
  • Los SDK siguen justificando su coste para el deferred deep linking, la atribución de instalaciones y la unión de identidades. Prescinde de ellos si no los necesitas.

Cómo decide el sistema operativo abrir tu app

Ambos sistemas afirman una relación verificada entre un dominio y una app. La documentación de Android dice que los App Links permiten que las URL verificadas de un sitio web "immediately open corresponding content in your Android app, without requiring the user to select your app from a disambiguation dialog", y que los usuarios sin la app "go to your website instead - no 404s, no errors" (developer.android.com/training/app-links/about, consultado el 2026-10-24).

Android verifica en el momento de la instalación. Cuando un filtro de intents lleva android:autoVerify="true", un dispositivo con Android 6 o posterior comprueba los hosts declarados contra https://hostname/.well-known/assetlinks.json mientras se instala la app (developer.android.com/training/app-links/verify-android-applinks, consultado el 2026-10-24). Espera al menos 20 segundos para esa comprobación antes de probar. En Android 15 y posteriores, la reverificación también se ejecuta periódicamente, y los cambios en el archivo pueden tardar hasta siete días en propagarse. Android 14 y anteriores recogen los cambios solo al instalar o actualizar.

Las páginas para desarrolladores de Apple (developer.apple.com, Supporting associated domains y Supporting universal links) no se renderizaron en mi descarga del 2026-10-24, así que no cito el comportamiento de Apple más allá de lo que muestran sus ejemplos publicados: el archivo vive en /.well-known/apple-app-site-association, la app declara applinks:your-domain en su entitlement Associated Domains y un Universal Link llega a la app como una actividad de usuario de navegación web que lleva la URL. Comprueba la página actual de Apple antes de publicar, sobre todo en lo que respecta a la caché y al comportamiento del modo de desarrollo.

La consecuencia para los enlaces cortos se sigue directamente. El sistema operativo decide con la URL que se pulsó. Eso ocurre antes de cualquier petición a tu servidor. Si el dominio de esa URL está verificado para tu app, la app se abre con esa URL. No se hace ninguna petición HTTP al enlace corto, así que no se ejecuta ninguna redirección.

Los dos archivos

Un archivo por plataforma, cada uno en una ruta fija del mismo dominio.

apple-app-site-association

El archivo AASA debe servirse en https://yourdomain.example/.well-known/apple-app-site-association. Sírvelo por HTTPS con una cadena de certificados válida, un estado 200 y Content-Type: application/json, y sin redirección.

La referencia del formato de Apple es Supporting associated domains; su página no se renderizó para mí el 2026-10-24, así que trata el ejemplo de abajo como la forma comúnmente publicada y compruébalo con esa página.

Una forma mínima de AASA:

{
  "applinks": {
    "details": [
      {
        "appIDs": ["ABCDE12345.com.example.acme"],
        "components": [
          {
            "/": "/spring-*",
            "comment": "Match any path starting with /spring-"
          },
          {
            "/": "/campaigns/*"
          }
        ]
      }
    ]
  }
}

appIDs es la concatenación del Team ID de Apple y el identificador de bundle de tu app, separados por un punto. El array components controla qué rutas activan el enrutamiento a la app; todo lo que no coincide con un componente pasa al navegador. Puedes registrar varias apps en el array details, útil si tienes una variante de consumo y otra empresarial del mismo producto en el mismo dominio.

Un detalle que conviene decir claramente: "/" con un patrón comodín como /spring-* es una coincidencia por prefijo de ruta. La sintaxis de patrones de la misma referencia incluye * (cualquier subcadena), ? (cualquier carácter) y objetos de exclusión. Si quieres coincidir con todas las rutas del dominio, usa "/" : "/*". Si quieres excluir una ruta concreta del enrutamiento a la app - por ejemplo, tu página /account/delete debe abrirse siempre en el navegador - añade un objeto de exclusión antes del comodín:

{
  "/": "/account/delete",
  "exclude": true
}

Las reglas se evalúan de la primera a la última, así que pon las exclusiones antes de los comodines.

assetlinks.json

El archivo Digital Asset Links de Android vive en https://yourdomain.example/.well-known/assetlinks.json. Google documenta el formato en developers.google.com/digital-asset-links/v1/getting-started.

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.acme",
      "sha256_cert_fingerprints": [
        "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"
      ]
    }
  }
]

package_name es el ID de aplicación de tu app en Play Store. sha256_cert_fingerprints contiene las huellas SHA-256 del certificado de firma de tu app (la página de Google dice que los destinos de apps móviles se identifican por el hash del certificado y el nombre del paquete). Obtenla con keytool -list -v -keystore your.keystore, o desde los ajustes de firma de tu Play Console si Play firma la app por ti. Incluye todos los certificados con los que instales compilaciones, incluido el de depuración.

A diferencia del archivo AASA, el de Android no lleva reglas de ruta. La coincidencia de rutas para App Links se hace en el AndroidManifest.xml mediante <intent-filter> con android:pathPrefix, android:pathPattern. El archivo assetlinks.json afirma la propiedad del dominio; el manifiesto declara qué rutas gestiona la app.

Secuencia de resolución de AASA: pulsación del enlace corto, descarga por el sistema operativo en la primera instalación, acierto de caché en las pulsaciones posteriores

Cómo encaja un dominio de enlaces cortos

Un enlace como go.acme.example/spring-launch es solo una URL en un dominio. Da a go.acme.example los dos archivos de asociación y decláralo en la app, y una pulsación en ese enlace abre la app en los dispositivos donde está instalada. Ese es el truco, y también el inconveniente. La app recibe https://go.acme.example/spring-launch, no la página a la que redirige el enlace.

El código de tu app tiene entonces dos opciones. Una: comparar la ruta por sí misma (/spring-launch significa la pantalla de primavera), lo que funciona cuando controlas los slugs. Dos: preguntar a un servidor adónde apunta el enlace corto y enrutar según la respuesta. Elido no tiene un endpoint documentado para que una app resuelva un slug a su destino. La redirección pública simplemente responde con una 302. Hoy la primera opción es la fiable, y la carencia está en nuestro backlog.

En dispositivos sin la app, o cuando la pulsación ocurre en un lugar que no entrega los enlaces al sistema operativo, el enlace corto se comporta como siempre. El edge responde con la redirección (302 por defecto; un enlace puede configurarse con 301 o 307) y el navegador la sigue. Las reglas de segmentación de Elido pueden coincidir por sistema operativo, así que un enlace puede enviar a los visitantes de iOS a una página del App Store y a los de Android a Google Play cuando la app no está.

Esto es lo que Elido hace y no hace hoy para esto, comprobado con el código:

CapacidadEstadoDónde
Servir apple-app-site-association para un dominio personalizadoSíPega el JSON por dominio en el panel
Servir assetlinks.json para un dominio personalizadoSíEl mismo panel
Devolver 404 cuando un dominio no tiene archivo configuradoSíLos dominios sin app no exponen nada
Dirigir a visitantes de iOS y Android a URL distintasSíReglas de segmentación (coincidencia de SO)
Deferred deep linking tras la instalaciónNoUsa el SDK de un proveedor
Atribución de instalaciones por clicNoConsolas de las tiendas o un proveedor

Elido guarda el JSON que redactas y lo sirve sin modificar bajo las rutas /.well-known/ del propio dominio. Elido no genera por ti el Team ID, el ID de bundle ni la huella del certificado de firma; esos vienen de tu cuenta de Apple Developer y de tu clave de firma. La configuración del dominio en sí (CNAME y TLS) es la misma que en enlaces cortos con dominio personalizado, y la página de la función de deep links muestra el flujo en el panel. Para los equipos de producto que usan enlaces cortos para la incorporación, este patrón cubre los enlaces de invitación y de referidos sin añadir un SDK al binario. La mitad del problema en la que todavía no hay nada instalado es la que abordan los App Clip Codes en iOS.

Lo que un SDK sigue haciendo mejor

Tres cosas quedan fuera de lo que te da el sistema operativo.

Deferred deep linking. Un usuario pulsa antes de instalar. El sistema operativo no tiene una app a la que entregar la URL, así que la pulsación va al navegador y se pierde la intención. Los proveedores la recuperan con coincidencia probabilística sobre los datos de la petición o con un traspaso por portapapeles. El aviso de consentimiento de rastreo de Apple vuelve feos los casos límite. Elido no hace esto hoy.

Atribución de instalaciones. Sin un SDK todavía puedes ver las instalaciones en App Store Connect y Play Console, pero no puedes vincular una a una pulsación concreta de un enlace. Las señales relativas sobreviven: ejecuta experimentos con smart links y compara las instalaciones por campaña. La atribución por dispositivo necesita un proveedor.

Unión de identidades. Conectar una pulsación con una dirección de correo o un contacto del CRM es un proyecto de datos. Los proveedores mantienen un grafo de dispositivos para ello.

Si ninguna de esas tres cosas importa, las primitivas del sistema operativo bastan. Si una importa, contrata al proveedor solo para ese trabajo.

Matriz de funciones: las primitivas nativas del sistema operativo y un SDK de pago abren la app y recurren a la web, pero solo el SDK añade deferred deep linking, atribución de instalaciones por clic y un grafo de identidad multiplataforma

Recetario de configuración

Tres lugares que hay que acertar: el servidor, el proyecto de iOS y el manifiesto de Android.

Requisitos de HTTPS y de ruta

Sirve ambos archivos por HTTPS con un certificado que encadene con una CA raíz pública. La página de Digital Asset Links de Google dice que la lista de declaraciones "must" vivir en /.well-known/assetlinks.json y que las listas en otros sitios o con otros nombres no son válidas (developers.google.com/digital-asset-links/v1/getting-started, consultado el 2026-10-24). Esa página no dice si se siguen las redirecciones, así que sirve el archivo directamente con un 200 y no apuestes. El mismo consejo vale para el archivo de Apple.

iOS: entitlement Associated Domains

En Xcode, en Signing & Capabilities, añade la capacidad Associated Domains con el valor applinks:go.acme.example. Apple documenta un sufijo ?mode=developer para compilaciones de desarrollo que hace que el dispositivo descargue el archivo de tu servidor en lugar de pasar por la caché de Apple; no pude volver a comprobar esa página el 2026-10-24, así que confírmalo en la documentación de Apple. Gestiona el enlace entrante como una actividad de usuario de navegación web (onContinueUserActivity(NSUserActivityTypeBrowsingWeb) en SwiftUI, o el delegado continue userActivity en UIKit) y lee webpageURL.

Dentro de la actividad que debe gestionar los enlaces:

<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="https"
    android:host="go.acme.example"
    android:pathPrefix="/campaigns/" />
</intent-filter>

Android considera para la verificación automática los filtros con la acción VIEW, las dos categorías anteriores y un esquema http o https. En Android 11 y anteriores, el sistema convierte tu app en el gestor predeterminado solo si encuentra un archivo Digital Asset Links para cada host del manifiesto, así que un solo host defectuoso los rompe todos. En un dispositivo dado, solo una app puede estar asociada a un dominio a la vez.

Comprobar que funciona

En Android, deja al menos 20 segundos para la verificación tras la instalación, luego ejecuta adb shell pm get-app-links com.example.acme (Android 12 y posteriores) y busca verified. Puedes forzar una nueva ejecución con adb shell pm verify-app-links --re-verify com.example.acme. Resultados como none, denied o legacy_failure significan que el archivo o la huella son incorrectos.

Para el propio archivo, la API de Digital Asset Links de Google lista las declaraciones que puede leer de un sitio: https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://go.acme.example&relation=delegate_permission/common.handle_all_urls. Una respuesta vacía significa que Android no verificará la app.

Para ambas plataformas, descarga el archivo como lo haría un rastreador:

curl -si https://go.acme.example/.well-known/assetlinks.json | head -20
curl -si https://go.acme.example/.well-known/apple-app-site-association | head -20

Quieres un 200 y un cuerpo JSON. Elido envía application/json en ambos archivos. Los errores habituales son una errata en el Team ID o en el ID de bundle, una compilación de lanzamiento cuyo certificado de firma no está en la lista de huellas, y un archivo que solo existe en el apex mientras los enlaces usan un subdominio.

La contrapartida, dicha claramente

Prescindir del SDK de deep links elimina una dependencia del binario de la app y una relación de intercambio de datos de tu aviso de privacidad. No pondré una cifra de tamaño; varía según el proveedor y no medí ninguna. El coste: una pulsación antes de la instalación no puede vincularse a la instalación.

Para equipos en fase inicial en los que el coste por instalación de cada canal todavía no guía el presupuesto, empieza con las primitivas del sistema operativo y añade un proveedor de atribución cuando los datos pasen a ser accionables.

Relacionado en el blog

Preguntas frecuentes

¿Puedo hacer deep linking sin Firebase ni un SDK de terceros?

Sí, para el caso en que la app ya está instalada. Universal Links de iOS y App Links de Android necesitan un dominio que controles, un archivo JSON de asociación en ese dominio y unas pocas líneas en la app. Las propias preguntas frecuentes de Firebase Dynamic Links de Google señalan estos dos mecanismos como la opción para hacer deep linking a apps instaladas. A lo que renuncias es al deferred deep linking, es decir, abrir la pantalla correcta tras una instalación nueva.

¿Funcionan Universal Links y App Links con un dominio de enlaces cortos?

Sí, si el dominio de enlaces cortos sirve los archivos de asociación y la app declara ese dominio. El inconveniente es que el sistema operativo abre la app con la propia URL corta pulsada, así que la app tiene que convertir esa URL en una pantalla. No puede contar con que la redirección se haya ejecutado. Cuando la app no está instalada, la misma pulsación se carga en el navegador y se aplica la redirección normal.

¿Dónde van los archivos de asociación?

Android lee https://your-domain/.well-known/assetlinks.json, y la documentación de Digital Asset Links de Google dice que esa es la única ubicación válida. El archivo de Apple va en https://your-domain/.well-known/apple-app-site-association sin extensión de archivo. Sirve ambos por HTTPS con un certificado que encadene con una CA pública.

¿Por qué mi app de Android muestra un selector en lugar de abrirse directamente?

La apertura directa necesita un App Link verificado. Android comprueba el dominio contra assetlinks.json cuando se instala la app, y solo cuando un filtro de intents lleva android:autoVerify="true". Si el archivo falta, es incorrecto o indica un certificado de firma distinto del de la compilación instalada, el enlace se comporta como un deep link normal.

¿Qué es el deferred deep linking y puedo prescindir de él?

El deferred deep linking lleva el destino de un enlace a través de una instalación de la app, de modo que un usuario que pulsa antes de instalar aterriza igualmente en la pantalla correcta en el primer arranque. Universal Links y App Links no hacen nada en ese caso porque la app no está allí para recibir la pulsación. Puedes prescindir de él en flujos de incorporación donde aterrizar en la pantalla de inicio es aceptable, y añadir un proveedor solo si el enrutamiento del primer arranque mueve tus cifras.

¿Qué partes de esto gestiona Elido?

Elido sirve los archivos apple-app-site-association y assetlinks.json para dominios personalizados, con el JSON que pegas en los ajustes del dominio en el panel. También dirige a los visitantes de iOS y Android a destinos distintos con reglas de segmentación. No ofrece deferred deep linking ni atribución de instalaciones, así que usa el SDK de un proveedor si los necesitas.

Prueba Elido

Pega una URL, obtén un enlace corto

Sin registro. El enlace vive 30 días. Crea una cuenta para conservarlo.

Sin registro. Crea una cuenta gratis y obtén 50 enlaces al mes.

Prueba Elido

Acortador de URL alojado en la UE: dominios personalizados, análisis profundo y API abierta. Plan gratuito - sin tarjeta de crédito.

Etiquetas
mobile deep linking
universal links
app links android
deep linking without sdk
apple app site association
digital asset links

Seguir leyendo