Guide · 8 min read
Universal Links: The Setup That Converts Web Traffic to App Installs (2026)
You have a website. Some of those visitors are on iPhone or Android. A fraction already have your app installed; the rest don't. Universal Links and Smart App Banners are the mechanism that routes installed users directly into your app and converts uninstalled visitors into App Store downloads — but the setup is finicky enough that most indie implementations are broken by a single file-hosting mistake. Here's the exact setup, the three silent-failure modes, and how to measure a channel most analytics tools hide from you.
Universal Links and Smart App Banners: different jobs, both required
Universal Links and Smart App Banners solve different problems in the same web-to-app funnel, and most apps implement one without the other. A Universal Link is a standard HTTPS URL on your domain that iOS intercepts when your app is installed — instead of loading in Safari, it opens your app directly at the correct screen. A Smart App Banner is the sticky strip that appears at the top of Safari for users who don't have your app installed, showing your app icon, name, and an Install button that goes directly to the App Store.
The practical setup requires both working together. When a user who already has your app taps a link to your website, Universal Links handle the redirect silently — no browser, no App Store page, straight into the right in-app screen. When a first-time visitor lands on the same URL, the Smart App Banner recruits them toward an install. Without Universal Links, installed users land in Safari and re-navigate manually — friction that consistently erodes session frequency. Without the Smart App Banner, web visitors leave without any prompt to download.
Neither mechanism requires changes to your app's core navigation logic. Universal Links call the same URL-handling code you'd use for deep links from push notifications. Smart App Banners are a single meta tag in your page's <head>. The infrastructure investment is one correctly-hosted JSON file and two hours of Xcode configuration — after which web-to-app routing runs automatically on every page of your site.
The AASA file: exact path, format, and hosting requirements iOS checks
The apple-app-site-association (AASA) file is the JSON file hosted on your domain that tells iOS which app is authorized to intercept which URL paths. Every Universal Link depends on it. The file must live at https://yourdomain.com/.well-known/apple-app-site-association — with no .json extension on the filename, even though the body is valid JSON. It must be served over HTTPS with a valid certificate. Self-signed or expired certificates cause iOS to skip validation silently, leaving you with no Universal Links and no error message to diagnose it.
A minimal AASA file for a single app looks like this: { "applinks": { "apps": [], "details": [{ "appID": "TEAMID.com.yourapp.bundle", "paths": ["*"] }] } }. Replace TEAMID with your 10-character Apple Team ID (visible in the Apple Developer portal under Membership) and com.yourapp.bundle with your app's bundle identifier. The paths array accepts exact paths, wildcards (*), and NOT exclusion patterns. Using ["*"] intercepts every URL on your domain; narrow it to ["/products/*"] to avoid intercepting unrelated pages.
Starting with iOS 14, Apple moved AASA validation off the device and onto Apple's own CDN at app-site-association.cdn-apple.com. This means Apple fetches your AASA file when a user installs or updates your app — not on demand per link-tap. Changes to your AASA file don't take effect immediately for existing installs. Existing users pick up changes only on next app update or reinstall. Always test Universal Links with a freshly-installed build rather than one that's been on the device for several days.
3 AASA mistakes that silently break Universal Links without any error
The most common AASA mistake is hosting the file at the wrong path. The correct path is /.well-known/apple-app-site-association. Many tutorials from 2018–2022 instructed developers to place it at the root (/apple-app-site-association) — iOS accepts the root path as a fallback on older systems, but Apple's CDN validation as of iOS 14 checks the /.well-known/ path first. Use /.well-known/ exclusively and verify with curl -I https://yourdomain.com/.well-known/apple-app-site-association before testing on device. If you get a 404, your hosting config needs adjustment.
The second mistake is adding a .json extension to the filename. The file must be named apple-app-site-association with no extension — hosting it as apple-app-site-association.json breaks validation silently. The third mistake is a wrong Content-Type response header: the file should be served as application/json. Some hosting providers return text/plain for extensionless files by default. Set the header explicitly in your server configuration or CDN response rules. Both of these produce zero output — the Universal Link simply falls through to Safari as if the file doesn't exist.
The fourth issue — less a file mistake than an Xcode configuration gap — is a missing Associated Domains entitlement. In Xcode, under your target's Signing & Capabilities tab, add the Associated Domains capability and add an entry for applinks:yourdomain.com. Without this entitlement, iOS ignores the AASA file entirely. Check both the capability in your app target and in the Provisioning Profile in the Apple Developer portal — both need to list the entitlement, or the handshake fails.
Smart App Banner meta tag: the one-line setup that recruits uninstalled users
The Smart App Banner appears automatically at the top of Safari on iOS when a page includes the correct meta tag. Add this to every page's <head> where a visitor might want the app: <meta name="apple-itunes-app" content="app-id=1234567890">. Replace 1234567890 with your numeric App Store app ID — visible in App Store Connect under App Information. Safari renders the banner automatically, showing your app icon (the static version; animated app icons only appear inside the App Store listing itself), your app name, a star rating, and either Install or Open depending on whether the app is already installed.
The app-argument parameter makes the banner context-aware. Extend the meta tag content like this: content="app-id=1234567890, app-argument=https://yourdomain.com/products/123". When a user with your app installed taps Open in the banner, iOS delivers that URL string to your application:openURL: or SwiftUI's .onOpenURL handler — letting you deep-link to the specific in-app screen that matches the web page. Without it, the Open tap always lands on the app's home screen. The app-argument value is also the key to deferred deep linking, covered in the next section. Check that your listing's subtitle and promotional text are optimized too — they're what users see when the Install button takes them to your App Store page.
The Smart App Banner is iOS/Safari-exclusive. Chrome on iOS does not display it; neither does any browser on Android. For Android visitors, you'll need a custom in-page prompt or a third-party library (Branch, Adjust Smart Banner) that detects the OS and links to the correct store. For apps with both platforms, the most reliable approach is a first-party banner component that checks navigator.userAgent and renders a store-appropriate install CTA — avoiding any dependency on Safari's banner behavior on Android.
Deferred deep linking: preserve the user's intent through the App Store install
Without deferred deep links, a user who clicks a product page on your website, taps Install in the Smart App Banner, and opens your app for the first time lands on a blank home screen — the context from your web page is gone. Deferred deep linking closes this gap by encoding the originating URL in install referrer data, then delivering it to the app on first launch after install.
Apple's native infrastructure doesn't provide deferred deep linking out of the box. The two main paths: a third-party attribution SDK (Branch, AppsFlyer, Singular) that handles the URL → install → app handoff automatically, or a first-party clipboard approach where you write the destination URL to the device clipboard before redirecting to the App Store, then read it on first launch. The clipboard method is free and SDK-free, but iOS 16+ requires explicit user consent for clipboard reads, which introduces a permission prompt that some users dismiss. For apps that already use an attribution SDK, deferred deep links are usually a one-line configuration change. Make sure your App Store screenshot sizes are exporting correctly too — users who tap Install from a well-crafted screenshot sequence should land somewhere relevant.
For content-heavy apps — editorial, e-commerce, recipe, travel — deferred deep links are worth prioritizing before further ASO optimization. A user who installs from your recipes page and opens to see that exact recipe has a materially different first session than one who opens to a blank dashboard. Context-matched first sessions consistently correlate with higher Day-1 and Day-7 retention. The install is the first step; where the user lands next determines whether they stay.
Measuring web-to-app conversion: closing the analytics gap
Standard web analytics tools (Google Analytics, Plausible, Fathom) record the Smart App Banner tap as a session exit to the App Store — the subsequent install and first app open are invisible. Your app analytics records the install with no referrer data tying it to the web session. The result: the web-to-app channel appears to produce zero installs, even when it's driving a meaningful share of your daily volume.
Three approaches to close the gap: (1) Use Apple's Campaign Token parameter in your App Store URL and track it in App Store Connect Analytics — it attributes installs to custom sources. (2) Use a third-party smart banner with built-in attribution (Branch, AppsFlyer Smart Banners), which handles the App Store handoff automatically. (3) Build first-party attribution by encoding a session ID in the deferred deep link payload and logging it in your backend on first app launch. Option 3 is the most durable but requires a backend. For most indie apps, Option 1 provides good-enough directionality with zero SDK cost. You can explore your options further in the roundup of free and low-cost ASO tools — several include basic deep link and attribution features.
Even without exact attribution, compare the trend of web exit events (from your web analytics) against new install volume (from App Store Connect or Play Console) over the same date range. A correlated spike is directionally reliable. This channel frequently has the lowest cost-per-install of anything in an indie app's acquisition mix — organic web traffic that converts via a Smart App Banner has a CPI close to zero — which makes it worth measuring even approximately before deciding how much to invest in it.
Android App Links: the assetlinks.json counterpart for Google Play
Android App Links work on the same principle as iOS Universal Links. A JSON file on your domain authorizes your Android app to intercept matching HTTPS URLs. The file lives at /.well-known/assetlinks.json and contains your app's package name and the SHA-256 fingerprint of your signing certificate. Generate it via the Asset Links Tool in Google Play Console under App content → Deep links, which auto-fills your package name and signing fingerprint. Unlike iOS, Android validates the file on device — changes take effect without a reinstall, which makes testing faster.
Chrome on Android shows an 'Open in app' chip when a user visits a URL with a verified App Link. This appears in the Chrome address bar area, not as a standalone banner like iOS Safari's. Firebase Dynamic Links, which previously handled Android's equivalent of the Smart App Banner flow, was deprecated in August 2024 — Google now recommends first-party App Links for new implementations. When configuring the Play Store side of this, verify your Play Store feature graphic dimensions are correct — users tapping through from the App Links prompt to your store listing see the feature graphic first.
When both platforms are in scope, implement both apple-app-site-association (iOS) and assetlinks.json (Android) on the same domain. The files are independent — different formats, different validation mechanisms, different path patterns beyond the shared /.well-known/ directory. A single website hosting both handles the full cross-platform web-to-app flow: iOS users get Universal Links and a Smart App Banner in Safari; Android users get App Links and an Open-in-app chip in Chrome. Both are free and require no ongoing maintenance once correctly configured.
Start with the AASA file and the meta tag
The two-hour setup — host the AASA file correctly at /.well-known/apple-app-site-association and add the Smart App Banner meta tag to every page — covers the largest share of the conversion gap. These two steps alone route installed users back into the app and surface an Install button to every new iOS visitor in Safari.
Web traffic is often the highest-intent, lowest-cost install channel in an indie app's mix, and most of it currently converts to nothing. Fix the infrastructure first, measure what the channel produces, then decide how much to invest in the web presence itself.
Update your App Store listing in the screenshot editor →
Frequently asked questions
does universal links setup require app review?
No. Universal Links are configured through your website (the AASA file) and your Xcode project (Associated Domains entitlement). Neither the AASA file nor an entitlement addition triggers App Store review on its own. You do need to submit an app update if the Associated Domains entitlement hasn't been added yet — but once the entitlement is in the provisioning profile, changes to the AASA file's path patterns take effect on the Apple CDN without any new submission.
smart app banner not showing in safari — why?
Four common causes: (1) The app-id value is wrong — use only the 10-digit numeric ID from App Store Connect, not the bundle identifier. (2) The user already dismissed the banner on that domain — Safari suppresses it for a period after dismissal and there's no way to force it back programmatically. (3) The page is loaded in Chrome or Firefox, not Safari — the Smart App Banner is Safari-only on iOS. (4) The app is unavailable in the user's App Store region — the banner suppresses itself automatically when the app can't be installed.
what is the apple team id in the aasa file?
Your Apple Team ID is a 10-character alphanumeric string tied to your developer account — it looks like A1B2C3D4E5. Find it in the Apple Developer portal under Membership, or in Xcode under your target's Signing & Capabilities section. The appID value in the AASA file is always TeamID.BundleIdentifier — for example, A1B2C3D4E5.com.yourcompany.yourapp. Getting this wrong produces no error; Universal Links silently fall through to Safari as if the file doesn't exist.
can universal links open a specific screen inside the app?
Yes — this is the primary use case. iOS delivers the full original URL to your app via AppDelegate's <code>application:continueUserActivity:restorationHandler:</code> or SwiftUI's <code>.onOpenURL</code> modifier. Parse the path and query parameters to route to the correct in-app screen. A URL like <code>https://yourapp.com/items/456</code> can open directly to the item detail view for ID 456. Without implementing URL routing in your app, Universal Links open the app but always land on the home screen regardless of the path.
do universal links work when the app is not installed?
No. When the app isn't installed, iOS falls back to Safari and loads the URL as a normal web page. This fallback is the intended behavior — your website is the correct destination for uninstalled users. The Smart App Banner on that page is what drives the install conversion. Universal Links and Smart App Banners are complementary: Universal Links handle installed users, the Smart App Banner handles everyone else.