Glossary/Failure modes
AASA file not found
Definition
An AASA file not found error means Apple's content delivery network could not retrieve a usable apple-app-site-association file from a domain, which disables Universal Links for that domain entirely.
The error is reported by validators and by swcutil on macOS, and it is almost never literally true — in most cases the file exists and loads fine in a browser. Apple's CDN applies constraints a browser does not: it follows no redirects, sends no credentials, and rejects the response outright if the content type is wrong. What follows is every cause, ordered by how often it turns out to be the one.
Confirm what Apple sees, not what your browser sees
Before working through the list, get the two facts that eliminate most of it. Your browser follows redirects, sends cookies, and does not care about content type, so a URL that looks healthy in a browser tells you almost nothing.
# 1. What Apple's CDN holds. An empty response or a 404 here means the CDN
# never got a usable file from your origin.
curl -sS https://app-site-association.cdn-apple.com/a/v1/example.com
# 2. What your origin returns, headers only, following redirects so you can
# see whether there were any. You want ONE 200, and application/json.
curl -sSIL https://example.com/.well-known/apple-app-site-association| CDN response | Origin response | Where the problem is |
|---|---|---|
| Valid JSON | Valid JSON | Not the file — see why Universal Links still fail |
| 404 or empty | Single 200, application/json | CDN has not fetched yet, or is holding a stale failure. Wait, or use ?mode=developer |
| 404 or empty | 301 or 302 anywhere in the chain | Cause 1 below — redirects |
| 404 or empty | 200 but Content-Type: text/html | Cause 3 below — wrong content type or an SPA catch-all |
| 404 or empty | 403 | Cause 5 below — a WAF or bot rule |
| Stale content | Correct new content | Cause 9 — propagation, up to 24 hours |
The nine causes, in order
1. A redirect in front of the file. Apple's CDN does not follow redirects — a 301 is a failure, not a hop. The redirects that do this are almost always ones nobody wrote deliberately: an http → https upgrade, an apex-to-www canonicalisation, a trailing-slash normaliser, or a locale prefix rewriting /.well-known/… to /en/.well-known/…. Exempt the .well-known path from every redirect rule you have.
2. The `.well-known` directory was dropped in the build. Dot-prefixed directories are excluded by default by many bundlers, static-site generators, upload tools, and Docker .dockerignore defaults. The file is in your repository, the deploy succeeds, and the directory simply is not in the artifact. Check the deployed output, not the source tree.
3. The wrong `Content-Type`, or a single-page-app catch-all. The response must be application/json. Two things commonly break this: a static server that assigns application/octet-stream to extensionless files, and an SPA or framework router that returns index.html with a 200 for any unmatched path. The second is nastier, because the URL returns 200 and looks alive while serving HTML.
4. The filename has an extension. apple-app-site-association.json is a different URL. The correct filename has no extension at all. This is common because editors add the extension on save, and because most tooling assumes JSON files end in .json.
5. A WAF, bot filter, or authentication wall. Apple's fetcher sends no cookies and no credentials, and it comes from Apple's infrastructure rather than a residential IP. Cloudflare's bot fighting mode, an enterprise WAF, HTTP basic auth on a staging domain, and Vercel's deployment protection all return 403 or a challenge page. Add an explicit allow rule for /.well-known/*.
6. The file is on a different host than the entitlement. example.com and www.example.com are distinct domains to iOS. If your entitlement says applinks:example.com but your site redirects the apex to www, cause 1 and cause 6 fire together. Host the file on both, and list both in the entitlement.
7. Invalid JSON, or valid JSON with the wrong shape. A trailing comma makes the whole file unparseable. More subtly, a file that parses but nests components one level too deep, or uses paths inside a components block, is discarded without an error. The CDN does not report shape problems — it just holds nothing.
8. The file exceeds 128 KB. Enforced as a hard cap. Above it the file is ignored in full, so every link on the domain fails simultaneously rather than degrading. Enumerated path lists are the usual cause; replace them with wildcards in components.
9. It is correct, and you are looking at a cache. Apple's CDN refreshes on its own schedule — allow up to 24 hours. Separately, the device caches the association from when the app was installed and does not re-fetch because you edited the file. Both caches have to turn over before a fix is visible.
Clearing the caches on purpose
Once the origin is serving correctly, you have three ways to stop waiting. They apply to different layers, and only the third affects real users.
| Method | Clears | Affects |
|---|---|---|
| Delete and reinstall the app | The device's cached association | Only that device |
?mode=developer in the entitlement | Bypasses the CDN entirely for that domain | Development builds only — never ship it |
| Waiting out the CDN | Apple's cached copy of your file | Everyone. The only one that fixes production |
# Force a fetch and print the reason for any failure
swcutil dl -d example.com
# Show what the system currently has associated
swcutil show --domain example.com
# Clear the local Shared Web Credentials cache, then re-fetch
sudo swcutil reset`?mode=developer` must not ship
It makes the device fetch your domain directly on every launch, which is useful in development and wrong in production — it removes the CDN's caching and puts the request on your origin. Remove it from the entitlement before archiving a release build.
A fix that stays fixed
Six of the nine causes above are infrastructure, not application code, which means they can regress from a change nobody associated with the app: a new CDN rule, a locale router, a WAF policy update. The file passing once is not evidence it will pass next month.
- Exempt
/.well-known/*from redirects, auth, and bot rules explicitly, in configuration, with a comment saying why. - Add a check to CI that asserts a single
200withContent-Type: application/json— acurl -sSI --max-redirs 0against production catches the redirect regression on the deploy that introduces it. - Serve the file from both the apex and
www, and list both in the entitlement, so a canonicalisation change cannot take links down. - Keep the file under 128 KB by using wildcards rather than enumerating paths, so the cap is never in play.
deep link debugger
Enter your domain and it runs the whole list above against the live site: the redirect chain, the content type, the size, the JSON shape, and what Apple's CDN is currently holding — plus the Android side. It reports which of the nine causes is yours rather than telling you the file is missing.
Open the deep link debugger →Frequently asked questions
- Why does my AASA file load in a browser but Apple says it is not found?
- A browser follows redirects, sends cookies, and ignores content type. Apple's CDN does none of those things — it requires a single 200 response with no redirects, no authentication, and a Content-Type of application/json. A file behind an http-to-https upgrade or an apex-to-www redirect loads perfectly in a browser and fails for Apple.
- Does Apple follow redirects to the apple-app-site-association file?
- No. Apple's CDN treats a 301 or 302 as a failure rather than following it. The request to https://yourdomain.com/.well-known/apple-app-site-association must return 200 directly. This is the single most common cause of a file that appears to be missing.
- How long does it take for AASA changes to take effect?
- Up to 24 hours for Apple's CDN to refresh its copy, and separately until the app is reinstalled or updated for an individual device to pick up the change. Adding ?mode=developer to the associated domains entitlement on a development build bypasses the CDN so changes are immediate, but it must be removed before release.
- How do I check what Apple's CDN has for my domain?
- Request https://app-site-association.cdn-apple.com/a/v1/yourdomain.com. That returns the exact copy devices are served. If it is empty or 404s while your own origin returns valid JSON, the CDN either has not fetched yet or was refused when it tried.
- Why did my Universal Links break after a deploy that did not touch the app?
- Most AASA failures are infrastructure rather than application code. A new redirect rule, a locale-prefix router, a WAF policy, a CDN configuration change, or a build pipeline that stops copying dot-directories will each break the file without any change to the app or to the file's contents.
- Can the AASA file be served from the root of my domain instead of .well-known?
- The root location is a legacy fallback from before iOS 9.3 and should not be relied on. Apple's CDN requests the .well-known path, so a new setup should serve it from https://yourdomain.com/.well-known/apple-app-site-association. Serving it at both locations is harmless.
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.
- Android App Links not working — Android App Links fail when the system cannot verify that a domain and an app belong to the same owner, at which point tapped links open in the browser instead of the app with no error shown to the user.