{"id":445,"date":"2026-03-14T17:00:00","date_gmt":"2026-03-14T22:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=445"},"modified":"2026-03-07T04:33:26","modified_gmt":"2026-03-07T09:33:26","slug":"aasa-file-setup","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/aasa-file-setup\/","title":{"rendered":"Apple App Site Association File: Complete Setup Guide"},"content":{"rendered":"\n<p>The Apple App Site Association file is the foundation of Universal Links on iOS. Without it, tapping a link to your domain opens Safari instead of your app. Get it right once, and iOS will route your links directly into the app experience you intended. Get it wrong, and you spend hours debugging a silent failure.<\/p>\n\n\n\n<p>This guide covers the complete setup: file format, JSON structure, path matching rules, serving requirements, and how to verify everything is working. If you are new to Universal Links in general, start with our <a href=\"https:\/\/tolinku.com\/blog\/universal-links-everything-you-need-to-know\/\">complete guide to Universal Links<\/a> for the broader picture before diving into AASA specifics.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What the AASA File Does<\/h2>\n\n\n\n<p>When a user installs your app, iOS fetches the AASA file from your domain and caches it. Later, when the user taps a link to your domain, iOS checks that cached file to determine whether your app should handle the URL. If the path matches a rule in the file, iOS opens the app. If it does not match, or if the file was never fetched successfully, the link opens in Safari.<\/p>\n\n\n\n<p>This lookup happens at install time (and periodically thereafter via Apple&#39;s CDN), not at tap time. That distinction matters for troubleshooting: if you update your AASA file, users with the app already installed will not see the change immediately. Apple&#39;s servers re-crawl the file on their own schedule.<\/p>\n\n\n\n<p>The file must live at one of two locations on your domain:<\/p>\n\n\n\n<pre><code>https:\/\/yourdomain.com\/.well-known\/apple-app-site-association\nhttps:\/\/yourdomain.com\/apple-app-site-association\n<\/code><\/pre>\n\n\n\n<p>Apple always checks the <code>.well-known\/<\/code> path first. The root path is the fallback for older iOS versions. You only need one, but serving both causes no harm. Either way, the file must be at the root of the domain (or subdomain) you register in your app&#39;s entitlements. You cannot nest it under a subdirectory.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">JSON Structure<\/h2>\n\n\n\n<p>The AASA file is a JSON document with no file extension. Here is the minimal structure for Universal Links:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;applinks&quot;: {\n    &quot;details&quot;: [\n      {\n        &quot;appIDs&quot;: [&quot;TEAMID.com.example.myapp&quot;],\n        &quot;components&quot;: [\n          {\n            &quot;\/&quot;: &quot;\/shop\/*&quot;,\n            &quot;comment&quot;: &quot;Match all shop URLs&quot;\n          }\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>A few things to note about this structure:<\/p>\n\n\n\n<p><strong>The <code>appIDs<\/code> field<\/strong> takes an array of strings in the format <code>TEAMID.bundleID<\/code>. Your Team ID is the 10-character alphanumeric string found in the <a href=\"https:\/\/developer.apple.com\/account\" rel=\"nofollow noopener\" target=\"_blank\">Apple Developer portal<\/a> under Membership Details. Your Bundle ID is the identifier you set in Xcode (for example, <code>com.example.myapp<\/code>). The combined value looks like <code>A1B2C3D4E5.com.example.myapp<\/code>.<\/p>\n\n\n\n<p><strong>The <code>details<\/code> array<\/strong> can contain multiple objects, each targeting different app IDs with different path rules. This is how you support multiple apps or multiple build targets (production, staging, beta) from a single AASA file.<\/p>\n\n\n\n<p><strong>The <code>components<\/code> array<\/strong> is the modern syntax introduced in iOS 13. Apple still supports the older <code>paths<\/code> syntax but recommends <code>components<\/code> for new implementations. The two are not interchangeable within a single entry.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">App ID Format<\/h2>\n\n\n\n<p>The App ID format trips up a lot of developers. It is always <code>TEAMID.bundleID<\/code>, never just the bundle ID. Two common mistakes:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Using the App ID prefix from the App IDs list in the developer portal instead of the actual Team ID. These can look similar but are not always the same value.<\/li>\n<li>Confusing the Team ID with the App ID itself. In the developer portal, navigate to Membership, not to Identifiers, to find the correct Team ID.<\/li>\n<\/ol>\n\n\n\n<p>You can verify your Team ID with a quick check:<\/p>\n\n\n\n<pre><code class=\"language-bash\"># From your provisioning profile\nsecurity cms -D -i ~\/Library\/MobileDevice\/Provisioning\\ Profiles\/your-profile.mobileprovision | grep -A1 TeamIdentifier\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Path Matching with Components<\/h2>\n\n\n\n<p>The <code>components<\/code> syntax gives you fine-grained control over which URLs trigger your app. Each object in the <code>components<\/code> array can have these keys:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>\/<\/code> : the path pattern to match<\/li>\n<li><code>?<\/code> : a query string pattern to match<\/li>\n<li><code>#<\/code> : a fragment pattern to match<\/li>\n<li><code>exclude<\/code> : set to <code>true<\/code> to exclude matching URLs (exclusions are evaluated in order)<\/li>\n<li><code>comment<\/code> : a human-readable label (ignored by iOS)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Wildcards<\/h3>\n\n\n\n<p>The <code>*<\/code> character matches any sequence of characters. The <code>?<\/code> character (when used inside a path pattern, not as the query key) matches any single character. For a deeper look at wildcard behavior, edge cases, and advanced pattern strategies, see our <a href=\"https:\/\/tolinku.com\/blog\/aasa-wildcards-and-paths\/\">guide to AASA wildcards and path matching<\/a>.<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;components&quot;: [\n    {\n      &quot;\/&quot;: &quot;\/products\/*&quot;,\n      &quot;comment&quot;: &quot;All product detail pages&quot;\n    },\n    {\n      &quot;\/&quot;: &quot;\/u\/*\/settings&quot;,\n      &quot;comment&quot;: &quot;User settings pages&quot;\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Exclusions<\/h3>\n\n\n\n<p>Exclusions let you carve out paths that should open in Safari even when they fall under a broader match. List exclusions before the broader rule they override:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;components&quot;: [\n    {\n      &quot;\/&quot;: &quot;\/products\/archived\/*&quot;,\n      &quot;exclude&quot;: true,\n      &quot;comment&quot;: &quot;Archived products open in browser&quot;\n    },\n    {\n      &quot;\/&quot;: &quot;\/products\/*&quot;,\n      &quot;comment&quot;: &quot;All other product pages open in app&quot;\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p>Order matters here. iOS evaluates <code>components<\/code> entries from top to bottom and stops at the first match. If you put the wildcard <code>\/products\/*<\/code> before the exclusion, the exclusion will never be reached.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Query String Matching<\/h3>\n\n\n\n<p>You can require specific query parameters as part of a match:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;components&quot;: [\n    {\n      &quot;\/&quot;: &quot;\/share&quot;,\n      &quot;?&quot;: { &quot;ref&quot;: &quot;?&quot; },\n      &quot;comment&quot;: &quot;Share URLs with any ref parameter&quot;\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p>The <code>?<\/code> value in the query object means &quot;any single character or more.&quot; You can also match exact values or use <code>*<\/code> for longer strings.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Matching Everything<\/h3>\n\n\n\n<p>To route all paths on a domain to your app, use a single wildcard:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;components&quot;: [\n    {\n      &quot;\/&quot;: &quot;*&quot;\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p>Be cautious with this. It means every link to your domain, including password reset emails and marketing unsubscribe pages, will try to open the app. A more targeted approach is usually better.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Multiple Apps in One File<\/h2>\n\n\n\n<p>If you have multiple apps that should handle links from the same domain, list them all in the <code>details<\/code> array:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;applinks&quot;: {\n    &quot;details&quot;: [\n      {\n        &quot;appIDs&quot;: [&quot;A1B2C3D4E5.com.example.consumer&quot;],\n        &quot;components&quot;: [\n          { &quot;\/&quot;: &quot;\/shop\/*&quot; },\n          { &quot;\/&quot;: &quot;\/account\/*&quot; }\n        ]\n      },\n      {\n        &quot;appIDs&quot;: [&quot;A1B2C3D4E5.com.example.driver&quot;],\n        &quot;components&quot;: [\n          { &quot;\/&quot;: &quot;\/driver\/*&quot; }\n        ]\n      },\n      {\n        &quot;appIDs&quot;: [\n          &quot;A1B2C3D4E5.com.example.consumer.beta&quot;,\n          &quot;A1B2C3D4E5.com.example.consumer.staging&quot;\n        ],\n        &quot;components&quot;: [\n          { &quot;\/&quot;: &quot;*&quot; }\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>The last entry in this example covers both a beta and staging build with a single rule by listing both app IDs in the <code>appIDs<\/code> array.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Serving Requirements<\/h2>\n\n\n\n<p>Apple&#39;s crawler has strict requirements for how the AASA file must be served. Any deviation causes a silent failure.<\/p>\n\n\n\n<p><strong>HTTPS only.<\/strong> The file must be served over HTTPS with a valid certificate. Self-signed certificates are not accepted. This applies to your production domain and any subdomain listed in your app&#39;s Associated Domains entitlement.<\/p>\n\n\n\n<p><strong>No redirects.<\/strong> Apple&#39;s crawler does not follow redirects. If your server redirects <code>\/.well-known\/apple-app-site-association<\/code> to another URL (even just adding a trailing slash), the crawl fails. Check your web server configuration, CDN rules, and any middleware that might issue automatic redirects.<\/p>\n\n\n\n<p><strong>Correct content type.<\/strong> Serve the file with <code>Content-Type: application\/json<\/code>. Some servers default to <code>application\/octet-stream<\/code> for files with no extension. You will need to explicitly set the content type.<\/p>\n\n\n\n<p>For Nginx:<\/p>\n\n\n\n<pre><code class=\"language-nginx\">location = \/.well-known\/apple-app-site-association {\n    default_type application\/json;\n}\n<\/code><\/pre>\n\n\n\n<p>For Apache:<\/p>\n\n\n\n<pre><code class=\"language-apache\">&lt;Files &quot;apple-app-site-association&quot;&gt;\n    ForceType application\/json\n&lt;\/Files&gt;\n<\/code><\/pre>\n\n\n\n<p><strong>File size limit.<\/strong> The AASA file must be 128 KB or smaller. In practice, even large files with hundreds of path rules stay well under this limit.<\/p>\n\n\n\n<p><strong>No authentication.<\/strong> The file must be publicly accessible. No HTTP basic auth, no IP restrictions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Xcode Entitlements<\/h2>\n\n\n\n<p>The AASA file alone is not enough. Your app must also declare the associated domain in its entitlements. In Xcode, go to your target&#39;s Signing and Capabilities tab, click the &quot;+&quot; to add a capability, and select Associated Domains. Add an entry in the format:<\/p>\n\n\n\n<pre><code>applinks:yourdomain.com\n<\/code><\/pre>\n\n\n\n<p>If you want to handle links on a subdomain, add that subdomain separately:<\/p>\n\n\n\n<pre><code>applinks:app.yourdomain.com\n<\/code><\/pre>\n\n\n\n<p>The entitlement and the AASA file must agree. If your entitlement points to <code>app.yourdomain.com<\/code>, Apple will look for the AASA file at <code>https:\/\/app.yourdomain.com\/.well-known\/apple-app-site-association<\/code>, not at your root domain. For a full walkthrough of the entitlement setup process, including provisioning profiles and development mode, see our <a href=\"https:\/\/tolinku.com\/blog\/associated-domains-entitlement\/\">Associated Domains entitlement guide<\/a>.<\/p>\n\n\n\n<p>For more on wiring up the Swift side of Universal Links, including the <code>application(_:continue:restorationHandler:)<\/code> delegate method and SwiftUI equivalents, see the <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/configuring-ios\/\">Tolinku iOS setup guide<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Validation<\/h2>\n\n\n\n<p>There are several ways to verify your setup before shipping to users.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Apple&#39;s Validator<\/h3>\n\n\n\n<p>Apple provides an <a href=\"https:\/\/branch.app.link\/e\/aasa-validator\" rel=\"nofollow noopener\" target=\"_blank\">AASA validator<\/a> at&#8230; actually, use the official tool at developer.apple.com or check via <code>aasa-validator<\/code> CLI tools. The most reliable official check is the App Search API validation tool in the Search tab of App Store Connect for indexed content, but for basic AASA validation, a direct curl check is often faster.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">curl Check<\/h3>\n\n\n\n<p>The fastest way to verify your file is reachable and correctly configured:<\/p>\n\n\n\n<pre><code class=\"language-bash\"># Check the file is reachable with the right content type\ncurl -sI https:\/\/yourdomain.com\/.well-known\/apple-app-site-association\n\n# Fetch and pretty-print the file contents\ncurl -s https:\/\/yourdomain.com\/.well-known\/apple-app-site-association | python3 -m json.tool\n\n# Check for redirects (should show no Location header)\ncurl -sIL https:\/\/yourdomain.com\/.well-known\/apple-app-site-association | grep -E &quot;HTTP\/|Location:&quot;\n<\/code><\/pre>\n\n\n\n<p>Look for <code>HTTP\/2 200<\/code> (or <code>HTTP\/1.1 200<\/code>) and <code>content-type: application\/json<\/code> in the response headers. Any <code>Location:<\/code> header indicates a redirect that will break Apple&#39;s crawler.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Apple&#39;s CDN<\/h3>\n\n\n\n<p>From iOS 14 onward, Apple routes AASA requests through its own CDN rather than having each device fetch your file directly. This means your file may be cached at Apple&#39;s edge and could lag behind your latest changes by hours. We cover this caching behavior in detail in our article on <a href=\"https:\/\/tolinku.com\/blog\/apple-cdn-validation-universal-links\/\">Apple CDN validation and Universal Links<\/a>. To force a re-fetch during development, you can delete and reinstall the app, or use the <code>applinks:yourdomain.com?mode=developer<\/code> entitlement value in debug builds. This developer mode bypasses Apple&#39;s CDN and fetches directly from your server.<\/p>\n\n\n\n<pre><code>applinks:yourdomain.com?mode=developer\n<\/code><\/pre>\n\n\n\n<p>Note: <code>?mode=developer<\/code> only works for apps installed via Xcode or TestFlight on devices registered to your team.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">On-Device Verification<\/h3>\n\n\n\n<p>After installing a debug build via Xcode, open the Console app on your Mac and filter for the process <code>swcd<\/code> (the service responsible for associated domains). You will see log entries showing whether Apple successfully fetched and parsed your AASA file, along with any errors.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common Mistakes<\/h2>\n\n\n\n<p><strong>Wrong App ID format.<\/strong> The single most common error. Double-check that you are using your Team ID, not your App ID prefix.<\/p>\n\n\n\n<p><strong>Redirect on the AASA URL.<\/strong> Even a harmless-looking redirect like HTTP to HTTPS, or a trailing slash added by the server, will cause the crawl to fail. Test with curl and watch for any redirect.<\/p>\n\n\n\n<p><strong>File served with wrong content type.<\/strong> Servers often default to <code>application\/octet-stream<\/code> for files without extensions. Explicitly set <code>application\/json<\/code>.<\/p>\n\n\n\n<p><strong>Entitlement and file domain mismatch.<\/strong> If the app entitlement says <code>applinks:app.yourdomain.com<\/code> but the AASA file is only at <code>yourdomain.com<\/code>, the link will not work.<\/p>\n\n\n\n<p><strong>Old <code>paths<\/code> syntax mixed with <code>components<\/code>.<\/strong> Apple&#39;s documentation deprecated the older <code>paths<\/code> array in favor of <code>components<\/code>. Do not mix them in the same entry.<\/p>\n\n\n\n<p><strong>Assuming changes propagate immediately.<\/strong> Apple caches the AASA file. Test with developer mode enabled to see your latest changes without waiting for the cache to expire. For a systematic approach to tracking down these and other issues, see our <a href=\"https:\/\/tolinku.com\/blog\/debugging-aasa-file\/\">AASA debugging guide<\/a>.<\/p>\n\n\n\n<p><strong>Missing the webcredentials or activitycontinuation sections.<\/strong> If you also need Shared Web Credentials or Handoff, those are separate top-level keys in the same AASA file. Omitting them when you need them is easy to overlook.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Putting It Together<\/h2>\n\n\n\n<p>Here is a complete, production-ready AASA file for a typical e-commerce app:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;applinks&quot;: {\n    &quot;details&quot;: [\n      {\n        &quot;appIDs&quot;: [&quot;A1B2C3D4E5.com.example.shop&quot;],\n        &quot;components&quot;: [\n          {\n            &quot;\/&quot;: &quot;\/checkout\/*&quot;,\n            &quot;exclude&quot;: true,\n            &quot;comment&quot;: &quot;Checkout handled in browser for payment security&quot;\n          },\n          {\n            &quot;\/&quot;: &quot;\/products\/*&quot;,\n            &quot;comment&quot;: &quot;Product detail pages&quot;\n          },\n          {\n            &quot;\/&quot;: &quot;\/collections\/*&quot;,\n            &quot;comment&quot;: &quot;Category and collection pages&quot;\n          },\n          {\n            &quot;\/&quot;: &quot;\/account\/*&quot;,\n            &quot;comment&quot;: &quot;Account and order history&quot;\n          },\n          {\n            &quot;\/&quot;: &quot;\/share\/*&quot;,\n            &quot;comment&quot;: &quot;Shared content&quot;\n          }\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>Once the file is in place, your server is configured to serve it correctly, and the entitlement is added in Xcode, Universal Links will work for every new install. For existing installs, the update propagates as Apple&#39;s CDN re-crawls your domain.<\/p>\n\n\n\n<p>For a broader look at how Universal Links fit into a deep linking strategy, including deferred links and analytics, see <a href=\"https:\/\/tolinku.com\/docs\/developer\/universal-links\/\">Tolinku&#39;s Universal Links documentation<\/a>. If you want to see how path matching and route configuration work in a managed setup, the <a href=\"https:\/\/tolinku.com\/features\/deep-linking\">deep linking features overview<\/a> covers how Tolinku handles AASA generation and hosting automatically.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Configure your AASA file correctly for Universal Links. Learn the JSON format, path matching, and how to validate your configuration.<\/p>\n","protected":false},"author":2,"featured_media":444,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Apple App Site Association File: Complete Setup Guide","rank_math_description":"Configure your AASA file correctly for Universal Links. Learn the JSON format, path matching, and how to validate your configuration.","rank_math_focus_keyword":"AASA file setup","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-aasa-file-setup.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-aasa-file-setup.png","footnotes":""},"categories":[12],"tags":[32,20,24,69,31,22],"class_list":["post-445","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ios","tag-apple-app-site-association","tag-deep-linking","tag-ios","tag-mobile-development","tag-swift","tag-universal-links"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/445","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=445"}],"version-history":[{"count":2,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/445\/revisions"}],"predecessor-version":[{"id":2771,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/445\/revisions\/2771"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/444"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=445"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=445"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=445"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}