Universal Link

A universal link is a standard HTTPS URL that opens a specific screen in an iOS app when that app is installed, and loads the equivalent web page when it is not. Apple introduced universal links in iOS 9 (September 2015) to replace custom URL schemes, which any app could claim without proving it owned the domain. The link only opens the app if Apple has verified the app and the website belong to the same owner.
https://example.com/product/12345
That single URL is the universal link. There is no special prefix and no second format. The same address works in a browser, an email, and an app.
Why Universal Links Matter
Universal links solve the ownership problem that custom URL schemes never could. A link like myapp://product/12345 can be registered by any app on the device, and iOS gives no guarantee about which one wins. A universal link is tied to a domain, and only an app whose team ID appears in a file hosted on that domain can claim it.
Three practical consequences:
- No dead ends. If the app is missing, the URL still resolves to a real web page. A custom scheme link fails to a blank page or an error.
- One link everywhere. No need to detect whether the app is installed before choosing which URL to send.
- It survives email and social. Mail clients and in-app browsers render an HTTPS link normally. Many strip or refuse to linkify a non-HTTPS scheme.
The trade-off is setup cost: a file on the domain, an entitlement in the app, and a verification pass you do not control the timing of.
How a Universal Link Works
Verification happens at install time, not at tap time. When an app declares an associated domain, the system fetches a file from that domain and checks whether the app is listed. A tap is then just a lookup against what was already verified.
- The app declares
applinks:example.comin its Associated Domains entitlement. - On install, the system fetches
apple-app-site-associationfrom that domain. - The system confirms the app’s team ID and bundle ID appear in the file.
- A tap on a matching URL opens the app. A non-matching path opens Safari.
Since iOS 14, Apple fetches the file through its own content delivery network rather than from each device. The site must therefore be publicly reachable: no basic auth, no bot challenge, no login wall, no geographic blocking. Apple’s CDN caches the result, and changes typically take 24 to 48 hours to propagate. During development, appending ?mode=developer to the entitlement value bypasses the CDN and fetches directly from the server.
The apple-app-site-association File
The apple-app-site-association file (often shortened to AASA) is a JSON file that declares which apps may open links on a domain. Host it at https://example.com/.well-known/apple-app-site-association.
{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.app"],
"components": [
{
"/": "/product/*",
"comment": "Opens product pages in the app"
},
{
"/": "/admin/*",
"exclude": true,
"comment": "Never open admin pages in the app"
}
]
}
]
}
}
Rules that break the file when ignored:
- No
.jsonextension. The filename is exactlyapple-app-site-association. - Serve
Content-Type: application/json. Servingtext/plainortext/htmlbreaks the fetch. - HTTPS with a valid certificate, and no redirects. A 301 to the file is a failure, not a detour.
- 128 KB maximum. Larger files are rejected.
appIDsis your team ID, a dot, then the bundle ID. The team ID is the 10-character prefix from your Apple Developer account.
The components array replaced the older paths array in iOS 13. Components match on path (/), query (?), and fragment (#), and support exclude and caseSensitive. If components is present, iOS 13 and later ignore paths entirely, so do not expect both to apply.
Universal Links vs Android App Links
Android App Links are the direct equivalent, using the same HTTPS URL and the same verify-by-domain principle. The files and the failure modes differ.
| Universal Link (iOS) | App Link (Android) | |
|---|---|---|
| Verification file | apple-app-site-association |
assetlinks.json |
| Location | /.well-known/ |
/.well-known/ |
| Identifies the app by | Team ID and bundle ID | Package name and SHA-256 signing fingerprint |
| App-side requirement | Associated Domains entitlement | android:autoVerify="true on an intent filter |
| Introduced | iOS 9 (2015) | Android 6.0 (2015) |
| If verification fails | Link opens in Safari | Link opens in the browser (Android 12+) |
Both are types of deep link, and both replace the older custom URI scheme approach. The Android side is stricter about signing: the fingerprint in assetlinks.json must match the certificate the app was actually signed with, so a Play-signed build and a locally-signed build need different entries.
Why Universal Links Stop Working
Most failures are hosting problems, not app problems. Check in this order.
The file is not reachable. Fetch it from outside your network. A staging password, a firewall rule, or a CDN that blocks unknown user agents will block Apple’s fetcher too.
Wrong content type, or a redirect. Both fail silently. The app simply never opens.
The path is not in components. A URL that matches no component falls through to Safari. This looks identical to a broken setup but is working as configured.
The user opted out. Tapping the breadcrumb banner in Safari’s top-right tells iOS to stop opening that domain in the app, and iOS remembers it. Long-press the link and choose Open in App to reverse it.
The link was typed or pasted into the address bar. Universal links only trigger from a tap. Entering the URL manually in Safari never opens the app, and neither does a link that points to the same domain as the page currently open. Both are intended behaviour, not bugs.
Frequently Asked Questions
What is a universal link?
A universal link is an ordinary HTTPS URL that opens an installed iOS app at a specific screen, and falls back to the matching web page when the app is not installed. Apple added the feature in iOS 9. It works only when the app and the website prove common ownership through the apple-app-site-association file hosted on the domain.
What is the difference between universal links and deep links?
A deep link is any URL that opens a specific location rather than a home screen, so a universal link is one kind of deep link. The distinction people usually mean is universal links versus custom URI scheme deep links. Custom schemes like myapp:// work only when the app is installed and can be claimed by any app, while universal links are verified against a domain and fall back to the web.
Where do I put the apple-app-site-association file?
Host it at https://yourdomain.com/.well-known/apple-app-site-association, over HTTPS, with no file extension and no redirects, served as application/json. It must be publicly reachable, because since iOS 14 Apple fetches it through its own CDN rather than from each device. Allow 24 to 48 hours for changes to propagate.
Are Android App Links the same as universal links?
They are the Android equivalent, built on the same idea but not interchangeable. Android verifies ownership through an assetlinks.json file listing your package name and signing certificate fingerprint, and the app must set android:autoVerify="true on its intent filter. Configuring iOS does nothing for Android, so both platforms need separate setup and separate testing.
Do universal links work with UTM parameters?
Yes. A universal link is a normal URL, so query parameters travel with it and the app reads them on open. Because no web request happens when the app opens, attribution depends on the app parsing those parameters rather than on a referrer.
To tag app-bound links with the same campaign vocabulary as your web links, build them with the free UTM builder at linkutm.