Un deep link es solo una URL que el sistema operativo entrega a una app en lugar de a un navegador. Toca el enlace en un dispositivo que tenga la app instalada, la app se abre en la pantalla correcta. Tócalo en un dispositivo sin la app, el navegador sigue la redirección al respaldo web. Una URL, dos resultados, sin shim de JavaScript, sin SDK de terceros requerido en la mayoría de las configuraciones.
Los equipos compran de más aquí rutinariamente. Branch.io, Adjust, AppsFlyer - sus materiales de marketing lideran con deep linking diferido: tocas el enlace antes de instalar la app, instalas, y la app se abre al contenido exacto a través del cual tocaste. Esa función es genuinamente compleja y requiere un truco de fingerprinting o emparejamiento de portapapeles del lado del servidor, porque el enrutamiento de enlaces del OS está latente hasta que la app se instala. Pero el deep linking diferido es una porción del problema. El caso más común, "el enlace se abre en la app ya instalada", se resuelve completamente por primitivas nativas del OS que Apple y Google enviaron en 2015 y 2015-16 respectivamente, y que funcionan con nada más allá de un dominio que controles y dos archivos JSON.
Este post trata sobre esos dos archivos JSON.
TL;DR#
- Apple Universal Links (iOS 9+) y Android App Links (Android 6.0+) manejan el flujo "abrir en app si está instalada, respaldo web si no" sin SDK de terceros.
- Ambos requieren un dominio que controles, servido sobre HTTPS, con un archivo de asociación validado en
/.well-known/. El OS obtiene y cachea el archivo en la instalación de la app, no en cada toque. - Un acortador de URL con un dominio personalizado como
go.acme.examplesirve ambos archivos y se convierte en el enlace que activa el enrutamiento de la app - el enlace corto es el deep link. - Lo que el SDK añade que las primitivas del OS no: deep linking diferido, atribución probabilística de instalación y stitching de identidad multiplataforma. Si necesitas esos, el SDK gana su costo. Si no los necesitas, estás pagando por funciones que no estás usando.
Las primitivas nativas del OS#
Apple introdujo Universal Links en iOS 9 (2015). Android envió App Links en Android 6.0 Marshmallow (también 2015, lanzado a dispositivos a través de 2016). Ambos siguen el mismo modelo conceptual: el OS afirma una relación verificada entre un dominio y una app, y cuando se toca una URL en ese dominio, el OS la enruta a la app en lugar del navegador.
La verificación es mutua y offline-first. En la instalación de la app, el OS obtiene un archivo de asociación de tu dominio y lo cachea. El fetcher de Apple está documentado en developer.apple.com/ios/universal-links/ (consultado el 2026-05-12); el equivalente de Google está en developer.android.com/training/app-links (consultado el 2026-05-12). Ningún fetch sucede en el momento del toque en un dispositivo caliente - la caché significa que la decisión de enrutamiento cuesta cero viajes de red.
La redirección en sí es un HTTP 302 estándar. El OS la intercepta antes de que el navegador cargue, comprueba su caché local y entrega la URL a la app si se encuentra una coincidencia. Una vez que la caché está caliente, toda la decisión es local. El edge que sirve el enlace corto emite la redirección y el OS toma el control.
Los dos archivos#
apple-app-site-association#
El archivo AASA debe servirse en https://yourdomain.example/.well-known/apple-app-site-association (Apple también comprueba la ruta del ápex https://yourdomain.example/apple-app-site-association para compatibilidad heredada, pero la ruta .well-known es el estándar actual). Debe servirse sobre HTTPS con una cadena de certificado válida y con una cabecera Content-Type: application/json. El fetcher del CDN de Apple rechaza archivos servidos con el Content-Type incorrecto - este es uno de los errores de configuración más comunes en producción.
La referencia completa del formato está en developer.apple.com/documentation/xcode/supporting-associated-domains.
Una forma AASA mínima:
{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.acme"],
"components": [
{
"/": "/spring-*",
"comment": "Match any path starting with /spring-"
},
{
"/": "/campaigns/*"
}
]
}
]
}
}
appIDs es la concatenación de tu Apple Team ID y el identificador de paquete de tu app, separados por un punto. El array components controla qué rutas activan el enrutamiento de la app; cualquier cosa que no coincida con un componente cae al navegador. Puedes registrar múltiples apps en el array details - útil si tienes una variante de consumidor y una empresarial del mismo producto en el mismo dominio.
Un detalle que vale la pena declarar claramente: "/" con un patrón comodín como /spring-* es una coincidencia de prefijo de ruta. El parser AASA de Apple soporta sintaxis de patrones definida en la documentación de Xcode, incluyendo * (cualquier substring), ? (cualquier carácter único), y objetos de exclusión. Si quieres coincidir con cada ruta en el dominio, usa "/" : "/*". Si quieres excluir una ruta específica del enrutamiento de la app - digamos, tu página /account/delete debería 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 primera a última. 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. La especificación es mantenida por Google 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 la Play Store. sha256_cert_fingerprints es la huella SHA-256 del certificado usado para firmar el APK - no el SHA-1, no el MD5. Puedes encontrar la huella en la Play Console bajo App Integrity, o ejecutando keytool -list -v -keystore your.keystore. Si lanzas tanto una build de debug como de producción, incluye ambas huellas en el array.
A diferencia del archivo AASA, el archivo de asociación de Android no soporta filtrado de ruta a nivel de archivo. La coincidencia de ruta para App Links se hace en AndroidManifest.xml vía <intent-filter> con android:pathPrefix, android:pathPattern o el más nuevo android:pathAdvancedPattern (disponible desde Android 12). El archivo assetlinks.json afirma la propiedad del dominio; el manifiesto declara qué rutas maneja la app.
Cómo encaja un acortador de URL#
Un enlace corto como go.acme.example/spring-launch es solo una URL en un dominio. Desde la perspectiva del OS, si go.acme.example tiene un archivo AASA o assetlinks.json válido, cualquier toque en un enlace bajo ese dominio es elegible para enrutamiento de app.
Esta es la configuración que soportamos directamente en dominios personalizados con Elido. Cuando registras go.acme.example como dominio personalizado en tu workspace, Elido sirve la redirección HTTPS para cada slug bajo ese dominio. Sirves los dos archivos de asociación desde el mismo dominio - ya sea en tu propio origen detrás de un proxy de ruta, o vía el propio servidor HTTPS del dominio. La redirección del edge se dispara; el OS la intercepta antes de que el navegador cargue, consulta su caché AASA/App Links y abre la app si se encuentra una coincidencia.
La arquitectura se describe en más detalle en el post custom-domains-for-short-links - la emisión TLS y la configuración CNAME se aplican aquí exactamente como se describe allí. La capa de deep-link es aditiva: mismo dominio, misma redirección, dos archivos JSON encima.
Para equipos de producto que usan enlaces cortos para onboarding móvil - códigos de referido, enlaces de invitación, flujos "compartir una receta" - este patrón cubre casi todo sin añadir dependencias de SDK al binario de la app.
Qué añade el SDK#
Tres capacidades que las primitivas nativas del OS no proporcionan:
Deep linking diferido. Un usuario toca tu enlace antes de instalar la app. En el primer lanzamiento después de la instalación, la app se abre al contenido exacto a través del cual tocaron. iOS Universal Links y Android App Links están silenciosos cuando la app no está instalada - la URL va al navegador, la intención se pierde. Recuperarla requiere una coincidencia de fingerprint del lado del servidor (IP + User-Agent + timestamp, probabilística) o el truco del portapapeles de iOS. Branch, Adjust y AppsFlyer ambos implementan estos; los casos extremos alrededor de los prompts de App Tracking Transparency y el comportamiento de Safari hacen no trivial hacerlo tú mismo.
Atribución de instalación a escala. La ruta nativa del OS te da la apertura de la app con la ruta de URL, pero si la app no estaba instalada en el primer toque, la cadena de atribución está rota. Reconciliar clics contra instalaciones vía SKAN en iOS y Play Install Referrer en Android es factible sin un SDK pago pero requiere trabajo de integración que los vendedores de atribución ya han hecho.
Stitching de identidad multiplataforma. Vincular un toque a una dirección de email, contacto CRM o sesión web. La ruta nativa del OS es anónima desde la perspectiva del servicio de enlaces. Los vendedores de SDK mantienen un gráfico de dispositivo persistente. Construir eso tú mismo es un proyecto sustancial de infraestructura de datos.
Si ninguno de esos tres se aplica, las primitivas del OS te cubren. Si uno importa, delimítalo precisamente - podrías solo necesitar deep links diferidos, una superficie de API, no el SDK completo.
Recetario de configuración#
Requisitos DNS y HTTPS#
Ambos archivos deben servirse sobre HTTPS desde el dominio cuyos enlaces quieres deep-link. El certificado debe encadenar a una raíz CA pública; los certs autofirmados causan que tanto los fetchers de validación de Apple como de Google fallen silenciosamente. Los certificados de Let's Encrypt funcionan bien.
El TLS del dominio tampoco debe redirigir la ruta /.well-known/ antes de servir el archivo. Si tu servidor emite una redirección www. antes de que el fetcher de Apple pueda alcanzar https://yourdomain.example/.well-known/apple-app-site-association, el fetch falla. El fetcher de Apple sigue hasta una redirección pero el fetcher de assetlinks de Google no sigue redirecciones en absoluto - el archivo debe estar en la ruta exacta, sin redirección.
iOS: Entitlement de Associated Domains#
En Xcode, bajo Signing & Capabilities de tu target, añade el entitlement Associated Domains con el valor applinks:go.acme.example. Si estás probando en una build de desarrollo (no distribuida vía TestFlight o la App Store), añade ?mode=developer al valor del entitlement: applinks:go.acme.example?mode=developer. Esto le dice al OS que vuelva a obtener el AASA en cada lanzamiento en lugar de usar la caché del momento de instalación - útil para iterar en tus patrones de ruta sin reinstalar desde la Store cada vez.
El CDN de Apple que obtiene tu archivo AASA es la propia infraestructura de Apple, no el dispositivo en sí. Apple pre-obtiene y cachea archivos AASA de tu dominio y los sirve a dispositivos durante la instalación de la app, lo que significa que el archivo necesita ser accesible para los crawlers de Apple, no solo para el dispositivo del usuario final. Esto también significa que hay un retraso de propagación - los cambios a tu archivo AASA pueden tardar horas en llegar a todos los dispositivos vía la caché de Apple. Para desarrolladores que necesitan iteración rápida en patrones de ruta, el entitlement ?mode=developer evita el CDN de Apple y obtiene directamente de tu servidor.
Android: App Links en el manifiesto#
En AndroidManifest.xml, dentro de la actividad que debería manejar los deep links:
<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:autoVerify="true" le dice a Android que intente verificación del dominio contra assetlinks.json. Sin él, el usuario ve una hoja de desambiguación en cada toque en lugar de una apertura directa a la app. La verificación sucede en la instalación; el dispositivo alcanza https://go.acme.example/.well-known/assetlinks.json y comprueba que la huella del certificado de la app instalada está listada.
Validadores y errores comunes#
Apple proporciona un validador en search.developer.apple.com/appsearch-validation-tool/ que comprueba si el CDN de Apple puede obtener y parsear tu archivo AASA. Ingresa tu dominio y devuelve ya sea un resultado de parseo válido o un error específico. Fallos comunes:
- Content-Type incorrecto. Si tu servidor devuelve
text/plainoapplication/octet-stream, el validador reporta el archivo como ilegible incluso si el JSON es válido. EstableceContent-Type: application/jsonexplícitamente. appIDsfaltante o desemparejado. El prefijo de Team ID enappIDsdebe coincidir con el Team ID en tu cuenta de Apple Developer exactamente, incluyendo el caso. Un solo carácter incorrecto falla silenciosamente en el momento del toque.- Problema de cadena de certificado. Si tu dominio sirve un certificado que no encadena a una raíz pública (común en entornos de staging con raíces CA locales), el fetcher de Apple rechaza el archivo.
El validador de Google es la API de Digital Asset Links: https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://go.acme.example&relation=delegate_permission/common.handle_all_urls. La respuesta JSON lista todas las declaraciones que Google ha verificado para tu dominio. Si la respuesta está vacía o falta el nombre de tu paquete, Android no auto-verificará la app en la instalación. Fallos comunes:
- Redirección en la ruta assetlinks. Como se señaló: el fetcher de Google no sigue redirecciones.
- Huella de certificado incorrecta. El APK de debug y el APK de release están firmados con claves diferentes. Si solo has listado la huella de release, las builds de debug no verificarán. Lista ambas.
- Archivo servido con CORS pero cabecera incorrecta para la petición de verificación. Al fetcher no le importa CORS, pero algunas configuraciones de CDN devuelven 403 en
GETdesde un rango de IP de Google si la ruta no está en la lista permitida de caché. Verifica que/.well-known/assetlinks.jsondevuelva 200 desde un cliente HTTP externo, no solo desde tu navegador.
La compensación declarada claramente#
Eliminar el SDK de deep-link ahorra aproximadamente 150-250KB de tamaño binario comprimido y elimina una llamada de API por instalación a los servidores del vendedor de atribución. Eso elimina una relación de compartición de datos de tu política de privacidad y puede simplificar los registros de procesamiento de datos GDPR. Victorias reales, pero modestas.
El costo es que la atribución en el momento de la instalación se vuelve aproximada o ausente. Si un usuario toca tu enlace antes de instalar la app, verás la instalación en App Store Connect o Play Console pero no la vincularás al toque específico del enlace. Aún puedes ejecutar experimentos de enlaces inteligentes para comparar qué campañas impulsan más instalaciones - las señales relativas sobreviven - pero la atribución por dispositivo, por clic requiere la capa de fingerprinting del SDK.
Para equipos en escala temprana donde el CPI por canal aún no está impulsando decisiones de presupuesto, comenzar con primitivas del OS es la ruta sensata. Añade un SDK de atribución más tarde, cuando los datos sean realmente accionables.
Para la guía de configuración orientada al desarrollador en la configuración de deep link de Elido, el esquema completo para el flujo de verificación de dominio, la configuración de proxy AASA y las opciones de servir assetlinks.json están documentadas allí. La página de soluciones para equipos de producto cubre los casos de uso más amplios de enlaces inteligentes y enlaces móviles.
Marius Voß es DevRel + edge infra en Elido. Posee los servicios edge-redirect y domain-manager.
Prueba Elido
Pega una URL, obtén un enlace corto
Sin registro. El enlace vive 30 días. Crea una cuenta para conservarlo.
Gratis, sin registro · 2 por día