AASA File Format for iOS Universal Links: The Complete Guide
Universal Links — iOS's mechanism for opening your app from ordinary HTTPS URLs — rest entirely on one JSON file. The Apple App Site Association file (universally called AASA) sits on your web server and declares which apps may open which paths on your domain. Get it right and your links glide from Safari, Messages, and Mail straight into the app. Get it wrong and iOS silently ignores you — no error, no log, just links that keep opening in the browser.
This is a working reference for the file: its format, its matching rules, its hosting requirements, and the failure modes that bite people.
What the AASA file is for
iOS will only hand an HTTPS URL to an app when it can prove the app and the website belong to the same party. The proof is the AASA file: served from your domain, it names your app's signing identity, and iOS verifies it when your app is installed or updated. If iOS later sees a link to a path the file covers, it opens your app instead of Safari — and if the app isn't installed, the URL simply loads your website. Universal Links never dead-end.
The JSON format
The current format (iOS 13 and later) looks like this:
{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.myapp"],
"components": [
{ "/": "/product/*", "comment": "Product pages open in app" },
{ "/": "/order/*", "comment": "Order tracking" },
{ "/": "/admin/*", "exclude": true, "comment": "Never open in app" },
{ "/": "/blog/*", "exclude": true }
]
}
]
},
"webcredentials": {
"apps": ["ABCDE12345.com.example.myapp"]
}
}
Under applinks.details, each entry pairs a list of appIDs with a list of components — the path rules. The order matters: iOS evaluates components top to bottom, so place your exclude rules before any broad wildcard that would otherwise match.
The appIDs field
Each app ID is your Apple Team ID concatenated with your bundle identifier: ABCDE12345.com.example.myapp. You can list several — a production app and a beta build, for instance — under one appIDs array, and multiple details entries can cover different apps with different path rules.
Path patterns and matching rules
The "/" component value matches the URL's path, with two wildcards:
*matches zero or more characters within a path component —/product/*matches/product/12345.?matches exactly one character.
Further rules worth knowing:
- A bare
{ "/": "*" }matches every path on the domain. exclude: trueblocks matching for that pattern — the modern replacement for the oldNOTprefix.- Matching is case-sensitive by default; set
caseSensitive: falseper component if you need lenient matching. - Query strings and fragments are ignored —
/product/12345?utm_source=emailmatches the/product/*rule on path alone.
The legacy format, briefly
Older AASA files (and older guides) use appID (singular, a string) and a paths array with NOT prefixes:
{ "applinks": { "apps": [], "details": [
{ "appID": "ABCDE12345.com.example.myapp", "paths": ["*", "NOT /admin/*"] }
] } }
iOS still understands it, but prefer the components format for new files — it's more expressive and it's where Apple's tooling is focused.
applinks vs. webcredentials
They live in the same file but do different jobs. applinks powers Universal Links — deep linking. webcredentials lets Safari and apps offer your website's passwords and passkeys in AutoFill. Including webcredentials doesn't affect deep linking either way; it's just convenient to co-locate.
Hosting requirements
This is where most AASA setups fail, and iOS fails silently:
- Exact path:
https://yourdomain.com/.well-known/apple-app-site-association— no.jsonextension. - HTTPS only. iOS will not fetch the file over plain HTTP.
- No redirects. The file must be served directly at that URL; a 301/302 elsewhere breaks verification.
Content-Type: application/json— some servers default toapplication/octet-stream, which iOS rejects.- Valid JSON, under 128 KB. Invalid JSON fails quietly; iOS caches the file, so a bad version can linger.
A complete example: two apps on one domain
{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.myapp"],
"components": [
{ "/": "/product/*" },
{ "/": "/order/*" },
{ "/": "/admin/*", "exclude": true },
{ "/": "/api/*", "exclude": true }
]
},
{
"appIDs": ["ABCDE12345.com.example.myapp.beta"],
"components": [
{ "/": "/product/*" }
]
}
]
}
}
Validating your file
- Check the raw response:
curl -I https://yourdomain.com/.well-known/apple-app-site-association— confirm 200, HTTPS,application/json, no redirect. - Validate the JSON — any linter; a trailing comma is all it takes.
- Check the entitlement: the app must carry
associated-domainswithapplinks:yourdomain.com, and the app ID in the file must match Team ID + bundle ID exactly. - Test on device: paste a link into Notes or Messages and long-press it — "Open in YourApp" should appear, and a plain tap should open the app, not Safari.
Troubleshooting common issues
- Links always open in Safari: usually a mismatch between the entitlement's domain and the file's location, or the file failing validation. Re-check with
curlbefore blaming the app. - Worked yesterday, broken today: iOS caches the AASA aggressively and re-fetches on app install/update or occasionally in the background. After changing the file, reinstall the app (or bump the build) to force a re-fetch.
- Wrong Content-Type: if your host serves the file as
octet-stream, fix the server config — the JSON itself is irrelevant until the header is right. - Some paths open the app, others don't: re-read your component order; an early broad wildcard can shadow a later rule, and a forgotten
excludeon an admin path opens it in the app by mistake.
Letting a platform host it for you
When you run your links through DecTog, the AASA file for your link domain is generated from your app's configuration in the dashboard — bundle ID and paths — and served at the correct path with the correct headers, so there's no JSON to hand-maintain and no silent-failure debugging. You keep ownership of the domain; DecTog just serves the file.
Related reading: assetlinks.json — the Android counterpart, and What is Deep Linking? for the wider context.