iOS · Universal Links

Universal Links not working? Your apple-app-site-association is probably redirecting

You tapped a link to your own site, expecting your app to open. Safari opened instead — no crash, no error, no log line. If you have spent an afternoon staring at a file that looks completely correct, there is a good chance the file is fine and the way your server delivers it is the problem.

The single most common cause of broken iOS Universal Links is a redirect sitting in front of your apple-app-site-association (AASA) file. Apple fetches it without following that redirect — so unless Apple's CDN already holds a valid cached copy, your association silently fails.

The symptom

30-second diagnosis

iOS expects your file at exactly this URL, over HTTPS, returned directly:

https://yourdomain.com/.well-known/apple-app-site-association

Fetch it the way Apple does — and crucially, without letting curl follow redirects:

curl -sS -D - -o /dev/null \
  https://yourdomain.com/.well-known/apple-app-site-association

Read the very first status line:

Why a redirect breaks everything

When you install or update an app that declares an applinks: Associated Domain, iOS asks Apple's CDN — not your origin server — for your AASA file. That service fetches the well-known URL and does not follow 3xx redirects. A 301 to the same file on www, an http → https upgrade, a trailing-slash normalization, a marketing-platform catch-all, an SSO wall that 302s anonymous requests — any of these returns a redirect status, and the association silently fails.

This is why the classic trap is apex ↔ www. You serve the app from example.com, but your host 301-redirects it to www.example.com. Your browser follows it and the page loads fine, so the redirect is invisible day to day. Apple's fetcher does not follow it, so Universal Links never associate.

The fix is not “add the file to the redirect target.” The file must be served directly, with a 200, at the exact host you list in your Associated Domains entitlement (applinks:example.com). If you support both apex and www, serve a valid 200 file on both hosts and list both domains.

The other failure modes (once the 200 is fixed)

A direct 200 is necessary but not sufficient. Then verify the file's contents:

It must be valid JSON, with no .json extension. The file is literally named apple-app-site-association — no extension. A parse failure shows as AASA_NOT_JSON.

Content-Type should be application/json. Modern iOS is lenient, but some proxies are not (AASA_WRONG_CONTENT_TYPE).

Your app ID must be listed. iOS reads app IDs from applinks.details[].appIDs. A minimal modern file:

{
  "applinks": {
    "details": [
      {
        "appIDs": ["ABCDE12345.com.yourcompany.yourapp"],
        "components": [
          { "/": "/products/*", "comment": "Product pages" }
        ]
      }
    ]
  }
}

The app ID is <TeamID>.<BundleID>. If applinks.details has no app IDs at all, Universal Links cannot work (AASA_NO_APPS). If the file is valid but your specific TeamID.BundleID is not in it, only your build fails (AASA_APPID_MISSING).

Keep it under 128 KB and don't gate it. Apple caps the file size (AASA_TOO_LARGE), and it must be publicly fetchable with no auth, no geo-block, and no bot challenge.

The subtle one: Apple's CDN cache and origin divergence

Because iOS reads your file through Apple's CDN, two things burn people:

Fix checklist