12 min de lectureFonctionnalités

Deep links pour applications mobiles sans SDK

Universal Links + Android App Links couvrent 80 % des cas d'usage de deep-linking sans SDK payant. Les deux fichiers d'association, les compromis et le livre de recettes

Marius Voß
DevRel · edge infra
Phone icon showing a deep-link flow from a tapped short link to an opened app with the iOS Universal Links and Android App Links logos juxtaposed

Un deep link n'est qu'une URL que le système d'exploitation passe à une application au lieu d'un navigateur. Tapez le lien sur un appareil qui a l'application installée, l'app s'ouvre au bon écran. Tapez-le sur un appareil sans l'app, le navigateur suit la redirection vers le repli web. Une URL, deux résultats, pas de shim JavaScript, pas de SDK tiers requis sur la plupart des configurations.

Les équipes surachètent ici régulièrement. Branch.io, Adjust, AppsFlyer - leur matériel marketing met en avant le deferred deep linking : vous tapez le lien avant d'installer l'app, vous installez, et l'app s'ouvre sur le contenu exact sur lequel vous avez tapé. Cette fonctionnalité est réellement complexe et exige un fingerprinting côté serveur ou un tour de presse-papier, parce que le routage de lien de l'OS est en sommeil jusqu'à ce que l'app soit installée. Mais le deferred deep linking est une tranche du problème. Le cas plus courant, « le lien s'ouvre dans l'app déjà installée », est résolu entièrement par des primitives natives de l'OS qu'Apple et Google ont livrées en 2015 et 2015-16 respectivement, et qui fonctionnent avec rien de plus qu'un domaine que vous contrôlez et deux fichiers JSON.

Cet article parle de ces deux fichiers JSON.

TL;DR#

  • Apple Universal Links (iOS 9+) et Android App Links (Android 6.0+) gèrent le flux « ouvrir dans l'app si installée, repli web sinon » sans SDK tiers.
  • Les deux exigent un domaine que vous contrôlez, servi en HTTPS, avec un fichier d'association validé à /.well-known/. L'OS récupère et met en cache le fichier à l'installation de l'app, pas à chaque tap.
  • Un raccourcisseur d'URL avec un domaine personnalisé comme go.acme.example sert les deux fichiers et devient le lien qui déclenche le routage de l'app - le lien court est le deep link.
  • Ce que le SDK ajoute que les primitives de l'OS n'ajoutent pas : le deferred deep linking, l'attribution d'installation probabiliste et le stitching d'identité multi-plateforme. Si vous avez besoin de ceux-ci, le SDK justifie son coût. Si vous n'en avez pas besoin, vous payez pour des fonctionnalités que vous n'utilisez pas.

Les primitives natives de l'OS#

Apple a introduit Universal Links dans iOS 9 (2015). Android a livré App Links dans Android 6.0 Marshmallow (aussi 2015, déployé sur les appareils jusqu'en 2016). Les deux suivent le même modèle conceptuel : l'OS affirme une relation vérifiée entre un domaine et une app, et quand une URL sur ce domaine est tapée, l'OS la route vers l'app plutôt que le navigateur.

La vérification est mutuelle et offline-first. À l'installation de l'app, l'OS récupère un fichier d'association depuis votre domaine et le met en cache. Le fetcher d'Apple est documenté à developer.apple.com/ios/universal-links/ (consulté le 2026-05-12) ; l'équivalent de Google est à developer.android.com/training/app-links (consulté le 2026-05-12). Aucun fetch ne se produit au moment du tap sur un appareil chaud - le cache signifie que la décision de routage coûte zéro aller-retour réseau.

La redirection elle-même est un HTTP 302 standard. L'OS l'intercepte avant que le navigateur ne charge, vérifie son cache local, et passe l'URL à l'app si un match est trouvé. Une fois le cache chaud, la décision entière est locale. L'edge servant le lien court émet la redirection et l'OS prend le relais.

Les deux fichiers#

apple-app-site-association#

Le fichier AASA doit être servi à https://yourdomain.example/.well-known/apple-app-site-association (Apple vérifie aussi le chemin apex https://yourdomain.example/apple-app-site-association pour la compatibilité legacy, mais le chemin .well-known est le standard actuel). Il doit être servi en HTTPS avec une chaîne de certificats valide et avec un en-tête Content-Type: application/json. Le fetcher CDN d'Apple rejette les fichiers servis avec le mauvais Content-Type - c'est l'une des erreurs de mauvaise configuration les plus courantes en production.

La référence de format complète est à developer.apple.com/documentation/xcode/supporting-associated-domains.

Une forme AASA minimale :

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

appIDs est la concaténation de votre Apple Team ID et de l'identifiant de bundle de votre app, séparés par un point. Le tableau components contrôle quels chemins déclenchent le routage app ; tout ce qui ne matche pas un component retombe sur le navigateur. Vous pouvez enregistrer plusieurs apps dans le tableau details - utile si vous avez une variante consumer et une variante enterprise du même produit sur le même domaine.

Un détail à dire clairement : "/" avec un pattern wildcard comme /spring-* est un match de préfixe de chemin. Le parser AASA d'Apple supporte la syntaxe de pattern définie dans la documentation Xcode, y compris * (n'importe quelle sous-chaîne), ? (n'importe quel caractère unique), et les objets d'exclusion. Si vous voulez matcher chaque chemin sur le domaine, utilisez "/" : "/*". Si vous voulez exclure un chemin spécifique du routage app - disons, votre page /account/delete devrait toujours s'ouvrir dans le navigateur - ajoutez un objet d'exclusion avant le wildcard :

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

Les règles sont évaluées de la première à la dernière. Mettez les exclusions avant les wildcards.

assetlinks.json#

Le fichier Digital Asset Links d'Android vit à https://yourdomain.example/.well-known/assetlinks.json. La spécification est maintenue par Google à 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 est l'application ID de votre app dans le Play Store. sha256_cert_fingerprints est l'empreinte SHA-256 du certificat utilisé pour signer l'APK - pas le SHA-1, pas le MD5. Vous pouvez trouver l'empreinte dans la Play Console sous App Integrity, ou en exécutant keytool -list -v -keystore your.keystore. Si vous publiez à la fois un build debug et un build production, incluez les deux empreintes dans le tableau.

Contrairement au fichier AASA, le fichier d'association d'Android ne supporte pas le filtrage de chemin au niveau du fichier. Le matching de chemin pour App Links se fait dans le AndroidManifest.xml via <intent-filter> avec android:pathPrefix, android:pathPattern, ou le plus récent android:pathAdvancedPattern (disponible depuis Android 12). Le fichier assetlinks.json affirme la propriété du domaine ; le manifest déclare quels chemins l'app gère.

Séquence de résolution AASA : tap sur un lien court, récupération par l'OS à la première installation, hit de cache lors des taps suivants

Comment un raccourcisseur d'URL s'intègre#

Un lien court comme go.acme.example/spring-launch est juste une URL sur un domaine. Du point de vue de l'OS, si go.acme.example a un fichier AASA ou assetlinks.json valide, tout tap sur un lien sous ce domaine est éligible au routage app.

C'est la configuration que nous supportons directement sur les domaines personnalisés avec Elido. Quand vous enregistrez go.acme.example comme domaine personnalisé dans votre workspace, Elido sert la redirection HTTPS pour chaque slug sous ce domaine. Vous servez les deux fichiers d'association depuis le même domaine - soit sur votre propre origin derrière un path proxy, soit via le propre serveur HTTPS du domaine. La redirection edge se déclenche ; l'OS l'intercepte avant que le navigateur ne charge, consulte son cache AASA/App Links, et ouvre l'app si un match est trouvé.

L'architecture est décrite plus en détail dans l'article custom-domains-for-short-links - l'émission TLS et la configuration CNAME s'appliquent ici exactement comme décrit là. La couche deep-link est additive : même domaine, même redirection, deux fichiers JSON par-dessus.

Pour les équipes produit utilisant les liens courts pour l'onboarding mobile - codes de parrainage, liens d'invitation, flux « partager une recette » - ce pattern couvre presque tout sans ajouter de dépendances SDK au binaire de l'app.

Ce que le SDK ajoute#

Trois capacités que les primitives natives de l'OS ne fournissent pas :

Deferred deep linking. Un utilisateur tape votre lien avant d'installer l'app. Au premier lancement après installation, l'app s'ouvre sur le contenu exact sur lequel il a tapé. iOS Universal Links et Android App Links sont silencieux quand l'app n'est pas installée - l'URL va au navigateur, l'intention est perdue. Récupérer ceci exige un match de fingerprint côté serveur (IP + User-Agent + timestamp, probabiliste) ou le tour du presse-papier iOS. Branch, Adjust et AppsFlyer les implémentent ; les cas limites autour des prompts App Tracking Transparency et du comportement Safari rendent le faire soi-même non trivial.

Attribution d'installation à l'échelle. La route native de l'OS vous donne l'ouverture de l'app avec le chemin URL, mais si l'app n'était pas installée au premier tap, la chaîne d'attribution est cassée. Réconcilier les clics contre les installations via SKAN sur iOS et Play Install Referrer sur Android est faisable sans SDK payant mais exige du travail d'intégration que les fournisseurs d'attribution ont déjà fait.

Stitching d'identité multi-plateforme. Lier un tap à une adresse email, un contact CRM ou une session web. La route native de l'OS est anonyme du point de vue du service de lien. Les fournisseurs SDK maintiennent un graphe d'appareils persistant. Construire cela vous-même est un projet d'infrastructure de données substantiel.

Si aucun de ces trois ne s'applique, les primitives de l'OS vous couvrent. Si un compte, scopez-le précisément - vous pourriez n'avoir besoin que de deferred deep links, une surface d'API, pas le SDK complet.

Matrice de fonctionnalités : les primitives natives de l'OS et un SDK payant ouvrent tous deux l'app et retombent sur le web, mais seul le SDK ajoute le deferred deep linking, l'attribution d'installation par clic et un graphe d'identité multi-plateforme

Livre de recettes de configuration#

Exigences DNS et HTTPS#

Les deux fichiers doivent être servis en HTTPS depuis le domaine dont vous voulez deep-linker les liens. Le certificat doit chaîner vers une racine CA publique ; les certs auto-signés font échouer silencieusement les fetchers de validation d'Apple et de Google. Les certificats Let's Encrypt fonctionnent très bien.

Le TLS du domaine ne doit pas non plus rediriger le chemin /.well-known/ avant de servir le fichier. Si votre serveur émet une redirection www. avant que le fetcher Apple ne puisse atteindre https://yourdomain.example/.well-known/apple-app-site-association, le fetch échoue. Le fetcher d'Apple suit jusqu'à une redirection mais le fetcher assetlinks de Google ne suit pas du tout les redirections - le fichier doit être au chemin exact, pas de redirection.

iOS : entitlement Associated Domains#

Dans Xcode, sous les Signing & Capabilities de votre cible, ajoutez l'entitlement Associated Domains avec la valeur applinks:go.acme.example. Si vous testez sur un build de développement (pas distribué via TestFlight ou l'App Store), ajoutez ?mode=developer à la valeur de l'entitlement : applinks:go.acme.example?mode=developer. Cela dit à l'OS de re-récupérer l'AASA à chaque lancement plutôt que d'utiliser le cache de l'installation - utile pour itérer sur vos patterns de chemin sans réinstaller depuis le Store à chaque fois.

Le CDN d'Apple qui récupère votre fichier AASA est l'infrastructure propre d'Apple, pas l'appareil lui-même. Apple pré-récupère et met en cache les fichiers AASA depuis votre domaine et les sert aux appareils pendant l'installation de l'app, ce qui signifie que le fichier doit être accessible aux crawlers d'Apple, pas seulement à l'appareil de l'utilisateur final. Cela signifie aussi qu'il y a un délai de propagation - les changements à votre fichier AASA peuvent prendre des heures pour atteindre tous les appareils via le cache d'Apple. Pour les développeurs qui ont besoin d'itérer vite sur les patterns de chemin, l'entitlement ?mode=developer contourne le CDN d'Apple et récupère directement depuis votre serveur.

Dans AndroidManifest.xml, à l'intérieur de l'activité qui devrait gérer les 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" dit à Android de tenter la vérification du domaine contre assetlinks.json. Sans lui, l'utilisateur voit une feuille de désambiguïsation à chaque tap plutôt qu'une ouverture directe vers l'app. La vérification se produit à l'installation ; l'appareil contacte https://go.acme.example/.well-known/assetlinks.json et vérifie que l'empreinte de certificat de l'app installée est listée.

Validateurs et erreurs courantes#

Apple fournit un validateur à search.developer.apple.com/appsearch-validation-tool/ qui vérifie si le CDN d'Apple peut récupérer et parser votre fichier AASA. Entrez votre domaine et il retourne soit un résultat de parse valide soit une erreur spécifique. Échecs courants :

  • Mauvais Content-Type. Si votre serveur retourne text/plain ou application/octet-stream, le validateur rapporte le fichier comme illisible même si le JSON est valide. Réglez Content-Type: application/json explicitement.
  • appIDs manquant ou non concordant. Le préfixe Team ID dans appIDs doit matcher le Team ID dans votre compte Apple Developer exactement, casse incluse. Un seul caractère faux échoue silencieusement au moment du tap.
  • Problème de chaîne de certificats. Si votre domaine sert un certificat qui ne chaîne pas vers une racine publique (courant dans les environnements de staging avec des racines CA locales), le fetcher d'Apple rejette le fichier.

Le validateur de Google est l'API 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 réponse JSON liste toutes les déclarations que Google a vérifiées pour votre domaine. Si la réponse est vide ou ne contient pas votre nom de package, Android n'auto-vérifiera pas l'app à l'installation. Échecs courants :

  • Redirection sur le chemin assetlinks. Comme noté : le fetcher de Google ne suit pas les redirections.
  • Mauvaise empreinte de certificat. L'APK debug et l'APK release sont signés avec des clés différentes. Si vous n'avez listé que l'empreinte release, les builds debug ne vérifieront pas. Listez les deux.
  • Fichier servi avec CORS mais mauvais en-tête pour la requête de vérification. Le fetcher ne se soucie pas de CORS, mais certaines configurations CDN retournent 403 sur GET depuis une plage IP Google si le chemin n'est pas dans la cache-allowlist. Vérifiez que /.well-known/assetlinks.json retourne 200 depuis un client HTTP externe, pas seulement depuis votre navigateur.

Le compromis énoncé clairement#

Supprimer le SDK de deep-link économise environ 150-250 Ko de taille de binaire compressée et élimine un appel API par installation aux serveurs du fournisseur d'attribution. Cela supprime une relation de partage de données de votre politique de confidentialité et peut simplifier les enregistrements de traitement de données RGPD. De vrais gains, mais modestes.

Le coût est que l'attribution à l'installation devient approximative ou absente. Si un utilisateur tape votre lien avant d'installer l'app, vous verrez l'installation dans App Store Connect ou Play Console mais ne pourrez pas la lier au tap de lien spécifique. Vous pouvez toujours faire tourner des expérimentations smart link pour comparer quelles campagnes pilotent plus d'installations - les signaux relatifs survivent - mais l'attribution par appareil, par clic exige la couche de fingerprinting SDK.

Pour les équipes à un stade précoce où le CPI par canal ne pilote pas encore les décisions de budget, commencer avec les primitives de l'OS est le chemin sensé. Ajoutez un SDK d'attribution plus tard, quand les données sont effectivement exploitables.

Avant-après de la suppression du SDK de deep-link : 150 à 250 Ko de binaire économisés et moins de flux de données à droite, contrebalancés par la perte de l'attribution d'installation par clic qui devient approximative

Pour le guide de configuration côté développeur sur la configuration deep link d'Elido, le schéma complet du flux de vérification de domaine, la configuration de proxy AASA et les options de service assetlinks.json sont documentés là. La page solutions pour les équipes produit couvre les cas d'usage smart link et de liens mobiles plus larges.


Marius Voß est DevRel + edge infra chez Elido. Il possède les services edge-redirect et domain-manager.

Essayer Elido

Collez une URL, obtenez un lien court

Sans inscription. Lien actif 30 jours. Inscrivez-vous pour le garder pour toujours.

Gratuit, sans inscription · 2 par jour

Essayer Elido

Raccourcisseur d'URL hébergé en UE : domaines personnalisés, analyses approfondies et API ouverte. Forfait gratuit - sans carte bancaire.

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

Lire la suite