Glossary/Implementation artifacts
Universal Link
Definition
A Universal Link is a standard HTTPS URL that opens an iOS app directly when that app is installed and the domain has authorised it, and loads the equivalent web page when it is not.
It replaced custom URI schemes as the default way to link into an iOS app, and the difference that matters is ownership: iOS only routes the URL to your app after checking a file on your own domain. That single property is why a Universal Link cannot be hijacked, degrades gracefully to the web, and is safe to put in an email, a QR code or an ad.
How the handshake works
There is no registry and no runtime negotiation. Two static declarations have to name each other, and iOS resolves the pairing once, at install time.
- Your app ships an Associated Domains entitlement containing
applinks:example.com. - Your domain hosts an apple-app-site-association file at
/.well-known/, listing your App ID and the path patterns it should handle. - At install, iOS asks Apple's CDN for that domain's file, checks that your App ID is in it, and caches the result.
- A tap on a matching HTTPS URL is handed to your app as an
NSUserActivitywithwebpageURLset. - If the app is not installed, or the path does not match, Safari loads the URL as an ordinary web page.
{
"applinks": {
"details": [
{
"appIDs": ["A1B2C3D4E5.com.example.shop"],
"components": [
{ "/": "/help/*", "exclude": true, "comment": "Support stays on web" },
{ "/": "/products/*", "comment": "Product pages open in the app" },
{ "/": "/orders/*", "?": { "guest": "true" }, "exclude": true },
{ "/": "/orders/*" }
]
}
]
}
}Exclusions must come first
components is evaluated top to bottom and stops at the first match. An exclude rule placed after a broad "/": "*" is unreachable, and the file looks entirely correct while doing the wrong thing. Order is the single most common logic error once hosting is right.
Where iOS deliberately ignores a Universal Link
A large share of "Universal Links are broken" reports are cases where iOS is behaving exactly as documented. These are the rules, and none of them indicate a misconfiguration.
| Context | Result | Why |
|---|---|---|
| URL typed or pasted into Safari's address bar | Web page | By design — the address bar is never a link tap |
| Link to the same domain as the current page | Web page | iOS assumes you are navigating within a site |
window.location without a user gesture | Web page | Programmatic navigation is not a tap |
| The user chose "Open in Safari" from the smart banner | Web page, permanently | A per-domain preference is stored until reset |
SFSafariViewController or WKWebView | Web page | In-app browsers do not resolve associations |
| Long-press → Open | Web page | The context menu's "Open in <App>" is the app route |
| App installed but never launched | Usually works | Association resolves at install, not first launch |
The per-domain preference is sticky and invisible
Once a user taps "Open in Safari" on the smart banner, iOS remembers it for that domain and stops offering the app — for that user, on that device, indefinitely. It is reset by long-pressing a link to the domain and choosing Open in <App>. This is the top cause of links that stopped working for one person and nobody else.
Universal Links against the alternatives
| Universal Link | App Link | Custom scheme | Deferred deep link | |
|---|---|---|---|---|
| Platform | iOS | Android | Both | Both |
| Format | HTTPS | HTTPS | myapp:// | HTTPS |
| Ownership verified | AASA file | assetlinks.json | None | Inherits the link's |
| App not installed | Opens the website | Opens the website | Error | Store, then routes after install |
| Hijackable | No | No | Yes | No |
| Works in the address bar | No | No | No | No |
| Needs a server | Yes | Yes | No | Yes |
Universal Links and App Links are the same idea implemented twice, which is why a cross-platform setup means hosting two files that describe the same routes. What neither of them does is survive an install: routing a user to content they clicked *before* the app existed is deferred deep linking, a separate mechanism layered on top.
Verifying one end to end
Check the CDN copy rather than your origin. The CDN copy is what devices are given, and it can be hours behind — or empty, if a fetch failed.
# 1. What Apple's CDN is serving devices right now
curl -sS https://app-site-association.cdn-apple.com/a/v1/example.com
# 2. Your origin: expect a single 200, application/json, no redirect
curl -sSIL https://example.com/.well-known/apple-app-site-association
# 3. macOS: force a fetch and print the reason it failed
swcutil dl -d example.com
swcutil show | grep -A 5 example.comThen confirm the entitlement that shipped in the binary, because a valid file paired with a missing entitlement produces exactly the same symptom:
codesign -d --entitlements :- /path/to/Shop.app | \
grep -A 5 associated-domainsIf both sides check out and specific URLs still fall through, the problem is path matching in components — usually a shadowed rule. If nothing opens at all, work through universal-links-not-opening-app.
Universal link tester
Paste your apple-app-site-association file and a list of URLs and it applies Apple's real matching rules — paths, query and fragment patterns, wildcards, case sensitivity and exclude — then names the rule that decided each outcome, including rules shadowed by an earlier catch-all.
Frequently asked questions
- What is the difference between a Universal Link and a deep link?
- Deep link is the general term for any URL that opens specific content inside an app. A Universal Link is Apple's specific implementation: a standard HTTPS URL that iOS routes to an app after verifying, through a file hosted on the domain, that the app is authorised to handle it. Every Universal Link is a deep link; not every deep link is a Universal Link.
- Why does my Universal Link open Safari instead of the app?
- Either iOS never created the association — the entitlement is missing, the apple-app-site-association file is unreachable or behind a redirect, or the App ID in it is wrong — or the tap happened in a context where iOS deliberately ignores Universal Links, such as the address bar, a same-domain link, or a webview. Check the CDN copy of the file first, then the entitlement in the built binary.
- Do Universal Links work if the app is not installed?
- Yes, and that is the point of using an HTTPS URL. With no app installed, the URL loads as an ordinary web page, so the same link is safe to put in an email, an ad or a QR code. Routing a user to that content after they install requires deferred deep linking, which is a separate mechanism.
- Why do Universal Links not work when pasted into Safari?
- Apple excludes address-bar navigation by design: typing or pasting a URL is treated as an explicit request for the web page, not a link tap. The same applies to programmatic navigation without a user gesture and to links pointing at the same domain as the current page. Test by tapping a real link from another domain, or from Notes or Messages.
- How do I test a Universal Link on a device?
- Send yourself the link in Notes or Messages and tap it — those are genuine link taps from a different context. On macOS, swcutil dl -d yourdomain.com forces a fetch of the association and prints the failure reason, and swcutil show lists what the machine currently holds. Avoid testing in the Simulator, which resolves associations unreliably.
- Can a Universal Link carry query parameters?
- Yes. The full URL, including its query string and fragment, is delivered to the app on the NSUserActivity as webpageURL, so campaign parameters survive the hop. The modern components format can also match on the query string, which lets you route some parameter values to the app and deliberately exclude others.
Related terms
- Apple App Site Association (AASA) — The apple-app-site-association file is a JSON document hosted at a domain's /.well-known/ path that tells iOS which app is allowed to handle which URLs on that domain.
- Associated Domain — An Associated Domain is an entry in an iOS app's com.apple.developer.associated-domains entitlement that declares a domain the app is bound to for a named service, such as Universal Links or shared web credentials.
- Android App Link — An Android App Link is an HTTPS URL that opens an Android app directly, without a chooser dialog, because the system has verified through a file on the domain that the app is authorised to handle it.
- Custom URI Scheme — A custom URI scheme is a non-standard URL protocol, such as myapp://, that an app registers with the operating system so that URLs beginning with it open that app.
- Universal Links not opening the app — A Universal Link fails to open its app when iOS has no valid association for the domain, when the tapped URL does not match the association's path rules, or when the tap did not originate in a context where iOS honours Universal Links at all.
- Universal Links stopped working — Universal Links that previously worked and no longer do have almost always been broken by a change outside the app: a per-domain user preference, a signing or infrastructure change, or Apple's CDN refreshing its cached copy of a file that was already broken.