Free Tool
4.9/5

Free Apple App Site Association Generator

Build the apple-app-site-association file iOS needs for Universal Links. Compose app IDs from your Team ID and bundle ID, add path, query, and fragment rules, and copy or download valid AASA JSON with the legacy paths array included.

This tool writes the file. Already hosting one and wondering why links fail? Run it through the AASA Validator. Building the Android half? Use the Android App Links Validator.

Not written into the file. We use it to show you the exact URL the file has to answer on.

App IDsTeam ID + bundle ID

The App ID prefix is usually your Team ID. Older identifiers can have a different prefix, so copy it from the Identifiers page in your Apple Developer account.

Path rules (components)First match wins
Rule 1
PATH "/"
QUERY "?"
FRAGMENT "#"
COMMENT

In patterns, * matches any run of characters and ? matches exactly one, so ?* means "at least one character".

Extra blocks

Universal Links still need a link to click

Wrap the campaign URLs that lead into your app in Flyn short links and see total clicks free on every one. Full country, device, and referrer breakdowns come with Pro.

Shorten for free

How to Create an AASA File in 3 Steps

Step 1: Enter your app IDs and path rules, Apple App Site Association Generator screenshot
1

Enter your app IDs and path rules

Your Team ID and bundle identifier compose the app ID that iOS matches, for example ABCDE12345.com.example.app. Add one rule per URL pattern: a path such as /products/*, an optional query match such as utm_source=?*, and an optional fragment. Turn on Exclude for the patterns that should stay in Safari, and reorder rules so exclusions sit above the broader pattern they carve out.

Step 2: Generate and download the file, Apple App Site Association Generator screenshot
2

Generate and download the file

You get formatted JSON with the components array iOS 13 and later reads, the legacy appID and paths keys older versions read, and optional webcredentials and appclips service blocks. The byte counter shows how much of the 128 KB budget you have used. Download saves an extensionless apple-app-site-association file, which is exactly what the server has to serve.

Step 3: Host it at /.well-known and verify, Apple App Site Association Generator screenshot
3

Host it at /.well-known and verify

Upload it so https://yourdomain.com/.well-known/apple-app-site-association answers with HTTP 200, Content-Type application/json, no redirect, and no auth in front of it. Then hand the live URL to our AASA validator, which fetches the file the way Apple does and tells you which of those conditions your server is actually meeting.

What the apple-app-site-association File Actually Does

A Universal Link is an ordinary https URL. There is nothing special about the link itself, so the association has to be proved from both sides. Your app claims the domain by listing applinks:example.com in its Associated Domains capability, and your domain confirms the claim by serving a file that names the app. iOS only opens your app for a tapped link when both halves agree.

The confirmation is this file. When your app is installed, the system fetches https://example.com/.well-known/apple-app-site-association through a content delivery network Apple runs, parses it, and keeps the result on the device. Later, when someone taps a link to that host, iOS walks the components array in order and acts on the first entry that matches the URL. A match with no exclude key opens the app; a match with "exclude": true sends the URL to Safari; no match at all also means Safari.

Two consequences follow from that ordering rule and catch people out. First, exclusions must come before the broader pattern they carve out, because matching stops at the first hit. Second, the file is fetched, not pushed: because it is cached on the device and behind Apple's CDN, an edit to the file does not reach installed apps immediately. During development, appending ?mode=developer to the associated domain makes the device fetch straight from your server and skip the cache.

The Hosting Checklist Everyone Gets Wrong

Most broken Universal Links are not caused by bad JSON. They are caused by a correct file that iOS cannot reach. Every row below is a condition the fetch has to satisfy.

RequirementWhat it has to beWhat breaks it
URL path/.well-known/apple-app-site-associationApple checks the .well-known directory first and the domain root second. Any other path is never requested.
File extensionNone at allapple-app-site-association.json is a different URL. The extensionless path returns 404 and every link falls back to Safari.
Content-Typeapplication/jsontext/plain, text/html, and application/octet-stream are common defaults when the server guesses from a missing extension.
HTTP status200, served directlyApple does not follow redirects. A 301 or 302 to www, to a trailing slash, or to a CDN path is treated as a failed fetch.
TransportHTTPS with a valid certificatePlain HTTP, an expired certificate, or a hostname mismatch stops the fetch before the file is read.
File sizeUnder 128 KB (131,072 bytes)Larger files are rejected. Wildcard patterns replace long literal path lists.
AccessPublic, unauthenticatedBasic auth, IP allowlists, geo blocks, staging passwords, and bot challenges all answer Apple instead of your file.
Edge and CDNPassed through untouchedA proxy that minifies, injects HTML, rewrites the content type, or serves an error page turns a valid file into an invalid one.
Host matchExactly the host in the entitlementexample.com and www.example.com are separate hosts. Each one you list as applinks: needs its own copy of the file.

This page writes the file, it does not fetch yours

Once the file is uploaded, check the live URL against every row in this table with our validator. It requests your file the way Apple does and reports the status code, redirects, content type, size, and parsed app IDs.

Open the AASA Validator

components vs the Legacy paths Array

iOS 13 replaced the string-based paths array with the components array. The two formats can live side by side in the same details entry, which is what the legacy toggle in the generator does, so one file keeps working across iOS versions.

Capabilitycomponents (iOS 13+)paths (legacy)
iOS versions that read itiOS 13 and lateriOS 12 and earlier (still parsed by newer versions when components is absent)
App ID key"appIDs", an array, so one entry can serve several apps"appID", a single string, one app per entry
Shape of a ruleA dictionary, for example {"/": "/products/*"}A plain string, for example "/products/*"
Match the query stringYes, via "?" with per-parameter patternsNo, the query is ignored entirely
Match the fragmentYes, via "#"No
Exclude a pattern"exclude": true on the ruleA "NOT " prefix on the string
Case sensitivityPer rule, "caseSensitive": false opts outAlways case sensitive, no control
Percent encodingPer rule, "percentEncoded": false opts outNo control
Inline documentationA "comment" key that iOS ignoresNone, the array holds strings only

One honest limit of shipping both: the legacy block names a single app and matches on the path only, so a rule that exists purely to match a query parameter or a fragment cannot be expressed there. The generator tells you exactly which rules were left out of the legacy array instead of quietly dropping them.

Every components Key, Explained

A component is a dictionary of matchers plus a few flags. Keys you leave out are simply not constrained, and the two boolean flags below default to true, which is why the generator only writes them when you opt out.

KeyWhat it matchesExampleDefault
"/"The URL path. * matches any run of characters, ? matches exactly one."/": "/products/*"No path constraint
"?"The query. A dictionary matches named parameters; a plain string matches the whole query string."?": { "utm_source": "?*" }No query constraint
"#"The fragment, written without the leading hash."#": "reviews"No fragment constraint
"exclude"When true, a match stops the URL from opening the app and hands it to Safari."exclude": truefalse
"caseSensitive"Whether the pattern is matched case sensitively."caseSensitive": falsetrue
"percentEncoded"Whether the pattern itself is written percent encoded."percentEncoded": falsetrue
"comment"Ignored by iOS. A note for whoever opens the file next."comment": "Product detail pages"None

Wildcards work the same way in every pattern: * matches any run of characters including none, and ? matches exactly one, so ?* means "at least one character" and ???? means "exactly four".

webcredentials and appclips in the Same File

applinks is only one of the services this file can declare, and they all live in the same JSON document at the same URL. Adding webcredentials lets Password AutoFill treat your website and your app as one login: a password saved in Safari is offered inside the app, and a password created in the app shows up on the site. It takes only the app IDs, no rules, and pairs with the Associated Domains entry webcredentials:example.com.

appclips names the App Clip that can launch from links on this domain. The App Clip has its own bundle identifier, conventionally the parent app's identifier with .Clip appended, so the generator prefills that and lets you change it. Turning these on adds a few lines, keeps everything at one URL, and means one deployment instead of three.

Everything the generator writes is computed in your browser. No app ID, bundle identifier, or path pattern is sent to a server, and your recent files are kept in your own browser storage so you can pull an earlier version back into the form. Clear that history whenever you like.

Where Universal Links Meet Campaign Links

One detail worth knowing before you launch a campaign: iOS evaluates the host of the URL that was actually tapped. If a link on one host redirects to your domain, the redirect target is not what gets matched against an association file, so a cross-domain hop is not a substitute for the entitlement. Any host you want to open your app has to appear in Associated Domains and serve its own copy of this file, a branded short domain included. Our deep link generator and deep link tester cover the scheme-based side of the same problem.

What short links are genuinely good at here is attribution. A Universal Link tells you nothing about where the tap came from, so put a distinct short link in each placement, the ad, the email, the QR code on the packaging, and the click counts tell you which one is feeding your app. The destination stays editable too, so when a path pattern changes you re-point the link instead of reprinting the material.

AASA Files + Flyn: Links You Can Measure

The generator is free forever. Add Flyn when you want the links that lead into your app to report back.

Editable destinations

Re-point a short link anytime. When a path pattern changes, fix every posted link with one edit.

Per-placement analytics

Total clicks free on every link; full country, device, and referrer breakdowns on Pro.

QR codes included

Turn any campaign link into a QR code for packaging, posters, and slides, free on every link.

A free plan that lasts

25 links a month with total click counts and QR codes. No trial countdown. Custom domains come with Pro.

Frequently Asked Questions

What is an apple-app-site-association file?
It is a JSON file you host on your own domain that tells iOS which URLs on that domain belong to your app. Your app declares the domain in its Associated Domains capability, and iOS reads this file to decide whether a tapped link opens the app or Safari. Without it, every link to your site opens in the browser no matter what your app can handle. The filename is literally apple-app-site-association, with no file extension at all.
Where exactly do I host the apple-app-site-association file?
At https://yourdomain.com/.well-known/apple-app-site-association. Apple looks in the .well-known directory first and falls back to the same filename at the domain root, so .well-known is the path to build for. Every host in your entitlement needs its own copy: www.example.com and example.com are different hosts to iOS, and so is a branded short domain. The file must answer with HTTP 200 over HTTPS at that exact URL.
Does the AASA file need a .json extension?
No, and adding one breaks it. iOS requests the exact path /.well-known/apple-app-site-association, so a file saved as apple-app-site-association.json sits at a different URL and the request comes back 404. Static hosts and build tools that append extensions automatically are a common cause of this. Configure a route or rewrite that serves the extensionless path directly.
What Content-Type should the AASA file be served with?
application/json. The body is plain unsigned JSON, and serving it as text/plain, text/html, or application/octet-stream is a classic cause of silent failure. Many servers guess the content type from the file extension, and because this file has none, they fall back to a default. Set the header explicitly for that one path.
Why do redirects break Universal Links?
Apple does not follow redirects when it fetches the association file. A 301 or 302 counts as a failed fetch even when it is benign, such as adding a trailing slash, forcing the www host, or pointing the path at a CDN. The request has to answer with HTTP 200 and the file body at the exact URL. This is also why an HTTP to HTTPS upgrade rule does not help: the fetch is HTTPS from the start.
What is the difference between components and the legacy paths array?
paths is the original format: an array of strings matched against the URL path only, with a NOT prefix for exclusions. components arrived in iOS 13 and replaced it with an array of dictionaries that match the path, the query, and the fragment separately, and can switch off case sensitivity or percent encoding per rule. Devices on iOS 13 and later read components, while iOS 12 and earlier understand only paths. Shipping both keys inside the same details entry keeps one file working across versions, which is what the legacy toggle in this generator does.
How do I find my Team ID and build the app ID?
The app ID in this file is the App ID prefix, a dot, then the bundle identifier, for example ABCDE12345.com.example.app. The prefix is normally your 10 character Team ID, shown in the membership section of your Apple Developer account. Identifiers created years ago can carry a different prefix, so if links fail with a Team ID that looks correct, copy the exact prefix from the Identifiers page instead. Wildcard App IDs do not work here, use the exact bundle identifier.
Can one AASA file cover more than one app?
Yes. The appIDs array inside a details entry can list several apps, and the details array itself can hold several entries with different rule sets. Use one entry with multiple app IDs when the same URLs should open whichever of your apps is installed, and separate entries when each app owns a different part of your site. The legacy paths block can name only one app, so devices on iOS 12 and earlier get the first app ID in the list.
How do I stop a specific path from opening the app?
Add a rule with that pattern and turn on Exclude, which writes "exclude": true into the component. iOS walks the components array in order and acts on the first entry that matches, so the exclusion has to sit above the broader rule it carves out. If /help/website/* should stay in Safari while /help/* opens the app, the exclusion goes first. The same idea in the legacy array is written as the string "NOT /help/website/*".
How do I match query parameters or fragments?
The "?" key takes a dictionary of parameter names mapped to value patterns, so {"utm_source": "?*"} matches any link carrying a non-empty utm_source. The "#" key matches the fragment, written without the leading hash. In both, * matches any run of characters and ? matches exactly one character, so "????" means exactly four characters. The legacy paths array cannot express either, which is why query-only and fragment-only rules are left out of the legacy block.
Does the file still need to be signed, and how large can it be?
It does not need a signature. iOS 9 dropped the CMS-signed requirement, so plain JSON served over HTTPS is the current expectation, and a signed blob is a leftover from very old guides. The size limit is 128 KB, which is 131,072 bytes. If you are approaching it, collapse long lists of literal URLs into wildcard patterns rather than enumerating every page.
My file looks right but Universal Links still do not work. What now?
Check delivery before contents: run the live URL through our AASA validator to confirm HTTP 200, no redirect, application/json, and parseable JSON on the real host. Then confirm the Associated Domains entitlement lists the same host as applinks:example.com, and that nothing in front of your server, such as basic auth, a WAF, a geo block, or a bot challenge, is answering Apple instead of your file. iOS fetches the file through a content delivery network Apple runs, so a changed file does not reach devices instantly, and reinstalling the app is the reliable way to pick it up. During development, adding ?mode=developer to the associated domain makes the device fetch straight from your server instead of the cache.

Ready to make your links open the app?

Generate the file for free, host it at /.well-known, then shorten the campaign links that lead into your app so you can see which placement actually drives opens. Analytics, QR codes, and editable destinations included.