iOS Universal Links: Opening Your App from a Web Link

Tapping a link on an iPhone can do two very different things: it can open a website in the browser, or it can open an app straight to the matching screen. A universal link is the mechanism that makes the second happen. When a user taps a universal link and the app is installed, iOS opens the app; when the app is not installed, the same link opens the website, so nobody hits a dead end. The iOS Universal Link Builder helps you assemble the pieces: the associated domain entries, the app identifiers, and the path patterns that decide which links open the app and which stay on the web. It builds configuration text from your input; it does not publish anything to your server or to Apple, and it cannot test a link on a device for you.

Three pieces must agree for a universal link to work. First, the app declares its associated domains, such as applinks:example.com, in its entitlements. Second, the website serves an association file at a fixed HTTPS location, listing the apps allowed to open its links. Third, the app handles the incoming link and routes it to the right screen. If any piece is missing or mismatched, iOS falls back to the browser, silently, with no error message to the user. That silent fallback is why universal links feel mysterious when they fail: everything looks fine, and the app simply does not open.

How it works

The association file is the piece most people get wrong, because its format is strict and its location is fixed. It must be served over HTTPS, without redirects, at the well-known path on the domain, and its contents must list each app by its team identifier plus bundle identifier, together with the path patterns that app handles. Path patterns use simple wildcards: a pattern can match a whole section of the site, a single path, or everything except certain paths. The builder helps you write these patterns consistently, but you must still publish the file yourself and confirm that your server serves it with the right content type and without a redirect to another page.

Patterns deserve care, because they decide the user experience. Matching everything means every link to your domain tries to open the app, including links to pages the app cannot display, which then need a graceful fallback. Matching too little means most links stay in the browser and the feature barely exists. A sensible starting point is the content section the app genuinely mirrors, such as product or article pages, while leaving help pages, checkout flows or legal pages on the web until the app can handle them.

Where it goes wrong

The classic failure is a mismatch between the team ID and the bundle ID in the association file and the values in the app itself; a single wrong character breaks the match. Another is serving the association file with a redirect, which iOS may refuse to follow, or behind authentication, which iOS cannot pass. A third is testing only in a simulator or a desktop browser and declaring success, when only a real device with the app installed exercises the real path. Also avoid assuming a link that worked once will work forever: if the association file changes, iOS re-fetches it on its own schedule, so a fix may take time to reach devices that already cached the old file.

Worked example

Worked example with sample values. Suppose your app handles 3 sections of a sample site: articles, products and user profiles. You write 3 path patterns: /articles/, /products/ and /users/. You also exclude 1 path, /products/preview/, which stays on the web. Counting the rules gives 3 allow patterns plus 1 exclusion, which is 4 rules in total. Your association file lists 1 app, identified by a sample team ID plus bundle ID pair, and the site serves the file at 1 fixed path. If a user taps 10 sample links, 7 matching the allow patterns and 3 matching the exclusion or other pages, then 7 links should open the app and 3 should stay in the browser, which is 7 divided by 10, or 70 percent, opening the app. These are sample values for illustration, not a live configuration, and the arithmetic shows how patterns, not luck, decide the outcome.

Mistakes to avoid

One mistake is forgetting the fallback: a universal link must still be a good web page for users without the app, or those users get a broken experience. Another is deep-linking to screens that require login without handling the logged-out state, which strands the user. A third is changing paths on the website without updating the patterns, so links that used to open the app quietly stop doing so. Keep the website, the association file and the app's routing in the same change whenever paths move, and test the old links as well as the new ones.

Putting it into practice

Build links in this order: define the paths the app truly handles, write the association file, publish it over HTTPS without redirects, add the associated domains to the app, and implement the routing with a web fallback for every screen. Then test on a real device, with the app installed and without it, and test a link to an excluded path to confirm it stays in the browser. Record the team ID, bundle ID and pattern list in one place, so the next person to touch the feature does not have to rediscover them. That checklist catches nearly every silent failure before users do.

Related tools and reading: the QR Code Generator and the URL Encoder and Decoder cover neighbouring checks that pair naturally with this one, and the guide at [/blog/website-seo-audit-guide/] extends the same topic with further background and examples for readers who want to go deeper.

Key points

  • A universal link opens your app when installed, else the website
  • The association file must be served over HTTPS at a fixed path
  • Each app identifier pairs a team ID with a bundle ID
  • Test on a real device; simulators and browsers alone mislead

In short: A universal link needs three agreeing pieces: the app's associated domains, the association file on the server, and routing inside the app. Publish the file over HTTPS without redirects, test on a real device, and keep a working web page as the fallback.

One habit ties all of this together: re-check after every change. A result is only as current as the input it was run on, so a fix, a redesign, or a new version of the underlying material deserves a fresh run rather than reliance on an old report. Keep the previous result alongside the new one, note what changed between them, and treat any new finding as a question to investigate rather than a verdict to accept or dismiss. That loop of checking, fixing, re-checking and recording is what turns a one-off test into a reliable routine, and it costs far less time than debugging a problem that a second check would have caught early.