Deeplinkly

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.

  1. Your app ships an Associated Domains entitlement containing applinks:example.com.
  2. Your domain hosts an apple-app-site-association file at /.well-known/, listing your App ID and the path patterns it should handle.
  3. At install, iOS asks Apple's CDN for that domain's file, checks that your App ID is in it, and caches the result.
  4. A tap on a matching HTTPS URL is handed to your app as an NSUserActivity with webpageURL set.
  5. If the app is not installed, or the path does not match, Safari loads the URL as an ordinary web page.
/.well-known/apple-app-site-association
{
  "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.

Contexts where a correctly configured Universal Link still does not open the app.
ContextResultWhy
URL typed or pasted into Safari's address barWeb pageBy design — the address bar is never a link tap
Link to the same domain as the current pageWeb pageiOS assumes you are navigating within a site
window.location without a user gestureWeb pageProgrammatic navigation is not a tap
The user chose "Open in Safari" from the smart bannerWeb page, permanentlyA per-domain preference is stored until reset
SFSafariViewController or WKWebViewWeb pageIn-app browsers do not resolve associations
Long-press → OpenWeb pageThe context menu's "Open in <App>" is the app route
App installed but never launchedUsually worksAssociation 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

The four ways a URL can reach a mobile app, compared.
Universal LinkApp LinkCustom schemeDeferred deep link
PlatformiOSAndroidBothBoth
FormatHTTPSHTTPSmyapp://HTTPS
Ownership verifiedAASA fileassetlinks.jsonNoneInherits the link's
App not installedOpens the websiteOpens the websiteErrorStore, then routes after install
HijackableNoNoYesNo
Works in the address barNoNoNoNo
Needs a serverYesYesNoYes

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.

The three checks that matter
# 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.com

Then confirm the entitlement that shipped in the binary, because a valid file paired with a missing entitlement produces exactly the same symptom:

Confirm the app's half
codesign -d --entitlements :- /path/to/Shop.app | \
  grep -A 5 associated-domains

If 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.

Open the universal link tester

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 DomainAn 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 LinkAn 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 SchemeA 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 appA 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 workingUniversal 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.