{"id":2051,"date":"2026-08-16T17:00:00","date_gmt":"2026-08-16T22:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=2051"},"modified":"2026-03-07T03:50:29","modified_gmt":"2026-03-07T08:50:29","slug":"aasa-wildcards-and-paths","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/aasa-wildcards-and-paths\/","title":{"rendered":"AASA Wildcards and Path Matching: Advanced Patterns"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The Apple App Site Association (AASA) file controls which URLs on your domain open your app via Universal Links. For simple apps, a single path pattern is enough. For complex apps with hundreds of routes, multiple subdomains, or paths that should explicitly not open the app, you need advanced path matching.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This article covers the full path matching syntax available in AASA files, including wildcards, exclusions, query parameter matching, and the differences between the legacy and modern AASA formats.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For AASA basics, see <a href=\"https:\/\/tolinku.com\/blog\/aasa-file-setup\/\">AASA file setup guide<\/a>. For Universal Links fundamentals, see <a href=\"https:\/\/tolinku.com\/blog\/universal-links-everything-you-need-to-know\/\">universal links: everything you need to know<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">AASA Format Versions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Apple introduced two AASA formats. Understanding which you are using matters because they handle path matching differently.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Legacy Format (iOS 9-12)<\/h3>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;applinks&quot;: {\n    &quot;apps&quot;: [],\n    &quot;details&quot;: [\n      {\n        &quot;appID&quot;: &quot;TEAMID.com.example.app&quot;,\n        &quot;paths&quot;: [&quot;\/product\/*&quot;, &quot;\/category\/*&quot;, &quot;NOT \/admin\/*&quot;]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Key characteristics:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Uses <code>appID<\/code> (singular) and <code>paths<\/code> array.<\/li>\n<li>Wildcards use <code>*<\/code> (matches any characters).<\/li>\n<li>Exclusions use <code>NOT<\/code> prefix.<\/li>\n<li>Paths are evaluated in order (first match wins).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Modern Format (iOS 13+)<\/h3>\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.app&quot;],\n        &quot;components&quot;: [\n          { &quot;\/&quot;: &quot;\/product\/*&quot;, &quot;comment&quot;: &quot;Product pages&quot; },\n          { &quot;\/&quot;: &quot;\/category\/*&quot;, &quot;comment&quot;: &quot;Category pages&quot; },\n          { &quot;\/&quot;: &quot;\/admin\/*&quot;, &quot;exclude&quot;: true, &quot;comment&quot;: &quot;Exclude admin&quot; }\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Key characteristics:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Uses <code>appIDs<\/code> (plural) and <code>components<\/code> array.<\/li>\n<li>Each component is an object with a <code>\/<\/code> key for the path.<\/li>\n<li>Exclusions use <code>&quot;exclude&quot;: true<\/code>.<\/li>\n<li>Supports query parameter and fragment matching.<\/li>\n<li>Components are evaluated in order (first match wins).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Apple recommends the modern format for new implementations. The legacy format is still supported for backward compatibility. Apple&#39;s documentation: <a href=\"https:\/\/developer.apple.com\/documentation\/xcode\/supporting-associated-domains\" rel=\"nofollow noopener\" target=\"_blank\">Supporting Associated Domains<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wildcard Syntax<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Both formats support two wildcard characters:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Character<\/th>\n<th>Meaning<\/th>\n<th>Example<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td><code>*<\/code><\/td>\n<td>Matches zero or more characters within a path component<\/td>\n<td><code>\/product\/*<\/code> matches <code>\/product\/123<\/code> and <code>\/product\/abc<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>?<\/code><\/td>\n<td>Matches exactly one character<\/td>\n<td><code>\/product\/??<\/code> matches <code>\/product\/ab<\/code> but not <code>\/product\/abc<\/code><\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">The <code>*<\/code> Wildcard<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>*<\/code> wildcard matches any sequence of characters, but its behavior depends on context:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;components&quot;: [\n    { &quot;\/&quot;: &quot;\/product\/*&quot; },\n    { &quot;\/&quot;: &quot;\/blog\/*\/comments&quot; },\n    { &quot;\/&quot;: &quot;\/*&quot; }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>\/product\/*<\/code> matches <code>\/product\/123<\/code>, <code>\/product\/shoes<\/code>, <code>\/product\/shoes\/red<\/code>.<\/li>\n<li><code>\/blog\/*\/comments<\/code> matches <code>\/blog\/my-post\/comments<\/code>.<\/li>\n<li><code>\/*<\/code> matches every path on the domain.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Important: <code>*<\/code> matches across path separators (<code>\/<\/code>). So <code>\/product\/*<\/code> will match <code>\/product\/shoes\/red\/size\/10<\/code>. If you only want to match a single path segment, you need a different approach (the <code>?<\/code> wildcard or more specific patterns).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The <code>?<\/code> Wildcard<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>?<\/code> wildcard matches exactly one character:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;components&quot;: [\n    { &quot;\/&quot;: &quot;\/item\/??&quot; },\n    { &quot;\/&quot;: &quot;\/v?\/api\/*&quot; }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>\/item\/??<\/code> matches <code>\/item\/ab<\/code> but not <code>\/item\/abc<\/code> or <code>\/item\/a<\/code>.<\/li>\n<li><code>\/v?\/api\/*<\/code> matches <code>\/v1\/api\/users<\/code> and <code>\/v2\/api\/users<\/code>.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Exclusion Patterns<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Exclusions tell iOS that certain paths should NOT open the app, even if they match a broader wildcard.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Modern Format Exclusions<\/h3>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;components&quot;: [\n    { &quot;\/&quot;: &quot;\/admin\/*&quot;, &quot;exclude&quot;: true, &quot;comment&quot;: &quot;Never open admin pages in app&quot; },\n    { &quot;\/&quot;: &quot;\/api\/*&quot;, &quot;exclude&quot;: true, &quot;comment&quot;: &quot;API endpoints stay in browser&quot; },\n    { &quot;\/&quot;: &quot;\/*&quot;, &quot;comment&quot;: &quot;Everything else opens the app&quot; }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Legacy Format Exclusions<\/h3>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;paths&quot;: [\n    &quot;NOT \/admin\/*&quot;,\n    &quot;NOT \/api\/*&quot;,\n    &quot;\/*&quot;\n  ]\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Evaluation Order Matters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Components and paths are evaluated in order. The first match wins. This means exclusions must come before the broader patterns they override:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;components&quot;: [\n    { &quot;\/&quot;: &quot;\/blog\/draft\/*&quot;, &quot;exclude&quot;: true },\n    { &quot;\/&quot;: &quot;\/blog\/*&quot; }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This excludes draft blog posts but includes all other blog paths. If you reversed the order, <code>\/blog\/*<\/code> would match first and the exclusion would never apply.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Query Parameter Matching (Modern Format Only)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The modern format supports matching on query parameters using the <code>?<\/code> key:<\/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;id&quot;: &quot;?*&quot; },\n      &quot;comment&quot;: &quot;Share links with an id parameter&quot;\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This matches <code>\/share?id=abc123<\/code> but not <code>\/share<\/code> without the <code>id<\/code> parameter. The <code>?*<\/code> pattern means &quot;one or more characters&quot; for the parameter value.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can match multiple parameters:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;components&quot;: [\n    {\n      &quot;\/&quot;: &quot;\/product&quot;,\n      &quot;?&quot;: { &quot;sku&quot;: &quot;?*&quot;, &quot;ref&quot;: &quot;?*&quot; },\n      &quot;comment&quot;: &quot;Product links with both sku and ref&quot;\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This requires both <code>sku<\/code> and <code>ref<\/code> parameters to be present.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fragment Matching (Modern Format Only)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The modern format also supports matching URL fragments (the part after <code>#<\/code>):<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;components&quot;: [\n    {\n      &quot;\/&quot;: &quot;\/page&quot;,\n      &quot;#&quot;: &quot;section-*&quot;,\n      &quot;comment&quot;: &quot;Page links with section fragments&quot;\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This matches <code>\/page#section-intro<\/code> and <code>\/page#section-details<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common Patterns<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">E-Commerce App<\/h3>\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.store&quot;],\n        &quot;components&quot;: [\n          { &quot;\/&quot;: &quot;\/checkout\/*&quot;, &quot;exclude&quot;: true, &quot;comment&quot;: &quot;Keep checkout in browser&quot; },\n          { &quot;\/&quot;: &quot;\/product\/*&quot;, &quot;comment&quot;: &quot;Product pages&quot; },\n          { &quot;\/&quot;: &quot;\/category\/*&quot;, &quot;comment&quot;: &quot;Category browsing&quot; },\n          { &quot;\/&quot;: &quot;\/cart&quot;, &quot;comment&quot;: &quot;Shopping cart&quot; },\n          { &quot;\/&quot;: &quot;\/order\/*\/track&quot;, &quot;comment&quot;: &quot;Order tracking&quot; },\n          { &quot;\/&quot;: &quot;\/sale&quot;, &quot;comment&quot;: &quot;Sale landing page&quot; }\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Content App<\/h3>\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.news&quot;],\n        &quot;components&quot;: [\n          { &quot;\/&quot;: &quot;\/admin\/*&quot;, &quot;exclude&quot;: true },\n          { &quot;\/&quot;: &quot;\/api\/*&quot;, &quot;exclude&quot;: true },\n          { &quot;\/&quot;: &quot;\/article\/*&quot;, &quot;comment&quot;: &quot;Articles&quot; },\n          { &quot;\/&quot;: &quot;\/author\/*&quot;, &quot;comment&quot;: &quot;Author profiles&quot; },\n          { &quot;\/&quot;: &quot;\/topic\/*&quot;, &quot;comment&quot;: &quot;Topic pages&quot; },\n          { &quot;\/&quot;: &quot;\/search&quot;, &quot;?&quot;: { &quot;q&quot;: &quot;?*&quot; }, &quot;comment&quot;: &quot;Search with query&quot; }\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Multi-App Domain<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If multiple apps share a domain, list them 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;TEAMID.com.example.consumer&quot;],\n        &quot;components&quot;: [\n          { &quot;\/&quot;: &quot;\/shop\/*&quot; },\n          { &quot;\/&quot;: &quot;\/product\/*&quot; }\n        ]\n      },\n      {\n        &quot;appIDs&quot;: [&quot;TEAMID.com.example.merchant&quot;],\n        &quot;components&quot;: [\n          { &quot;\/&quot;: &quot;\/merchant\/*&quot; },\n          { &quot;\/&quot;: &quot;\/dashboard\/*&quot; }\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">iOS checks each entry in order. The first app with a matching path wins.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Debugging Path Matching<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If your path patterns are not working as expected:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Test the AASA file directly.<\/strong> Fetch your AASA file and verify the JSON is valid:<\/p>\n\n\n\n<pre><code class=\"language-bash\">curl -s https:\/\/yourdomain.com\/.well-known\/apple-app-site-association | jq .\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Check evaluation order.<\/strong> Remember that components are evaluated top-to-bottom. An overly broad pattern early in the list can prevent more specific patterns from matching.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Verify wildcard behavior.<\/strong> The <code>*<\/code> wildcard matches across path separators. If you intended to match only one segment, your pattern may be matching more than expected.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Check Apple&#39;s CDN cache.<\/strong> Apple caches AASA files on its CDN. Changes may take time to propagate. See <a href=\"https:\/\/tolinku.com\/blog\/cdn-and-aasa-caching\/\">CDN and AASA caching<\/a> for details.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Use the AASA validator.<\/strong> Apple provides validation through Xcode&#39;s Associated Domains diagnostics. See <a href=\"https:\/\/tolinku.com\/blog\/debugging-aasa-file\/\">debugging AASA files<\/a> for step-by-step instructions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For testing Universal Links in general, see <a href=\"https:\/\/tolinku.com\/blog\/testing-universal-links\/\">testing Universal Links<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tolinku and AASA Path Configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/tolinku.com\/features\/deep-linking\">Tolinku<\/a> manages your AASA file automatically based on the routes you configure in the dashboard. When you create a route in Tolinku, the AASA file is updated to include the corresponding path pattern. This eliminates manual AASA editing and reduces the risk of syntax errors or evaluation-order mistakes. See the <a href=\"https:\/\/tolinku.com\/docs\/developer\/universal-links\/\">Universal Links developer guide<\/a> for configuration details.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For the complete Universal Links guide, see <a href=\"https:\/\/tolinku.com\/blog\/universal-links-everything-you-need-to-know\/\">universal links: everything you need to know<\/a>. For deep link parameter handling, see <a href=\"https:\/\/tolinku.com\/blog\/deep-link-parameters\/\">deep link parameters<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Master advanced path matching in AASA files for Universal Links. Configure wildcards, exclusions, and complex URL pattern rules.<\/p>\n","protected":false},"author":2,"featured_media":2050,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"AASA Wildcards and Path Matching: Advanced Patterns for Universal Links","rank_math_description":"Master advanced path matching in AASA files for Universal Links. Configure wildcards, exclusions, and complex URL pattern rules.","rank_math_focus_keyword":"AASA wildcards","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-wildcards-and-paths.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-wildcards-and-paths.png","footnotes":""},"categories":[12],"tags":[76,648,122,101,20,24,653,31,87,22],"class_list":["post-2051","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ios","tag-aasa","tag-app-development","tag-apple","tag-configuration","tag-deep-linking","tag-ios","tag-path-matching","tag-swift","tag-troubleshooting","tag-universal-links"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/2051","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=2051"}],"version-history":[{"count":1,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/2051\/revisions"}],"predecessor-version":[{"id":2052,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/2051\/revisions\/2052"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/2050"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=2051"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=2051"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=2051"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}