{"id":1830,"date":"2026-07-23T17:00:00","date_gmt":"2026-07-23T22:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=1830"},"modified":"2026-03-07T03:50:10","modified_gmt":"2026-03-07T08:50:10","slug":"link-wrapping-and-redirects","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/link-wrapping-and-redirects\/","title":{"rendered":"Link Wrapping and Redirects: Impact on Deep Links"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">You configure Universal Links perfectly. You test them. They work. Then you send a link in an email through Mailchimp, and it stops working. The reason: Mailchimp wrapped your URL for click tracking, and the wrapped URL points to <code>https:\/\/track.mailchimp.com\/...<\/code>, not your domain. Since the tracked URL is not associated with your app, Universal Links do not activate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is the link wrapping problem, and it affects almost every channel where deep links are distributed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For deep linking challenges more broadly, see <a href=\"https:\/\/tolinku.com\/blog\/deep-linking-challenges\/\">common deep linking challenges and how to solve them<\/a>. For the fundamentals, see <a href=\"https:\/\/tolinku.com\/blog\/complete-guide-deep-linking-2026\/\">the complete guide to deep linking in 2026<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Link Wrapping Works<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">The Wrapping Chain<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When an email service provider (ESP) wraps a link:<\/p>\n\n\n\n<pre><code>Original URL:   https:\/\/app.example.com\/products\/123\nWrapped URL:    https:\/\/track.esp.com\/click?id=abc&amp;url=https%3A%2F%2Fapp.example.com%2Fproducts%2F123\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When the user clicks the wrapped URL:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Browser opens <code>https:\/\/track.esp.com\/click?id=abc&amp;url=...<\/code><\/li>\n<li>ESP records the click.<\/li>\n<li>ESP redirects (301 or 302) to <code>https:\/\/app.example.com\/products\/123<\/code>.<\/li>\n<li>Browser loads the final URL.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">The problem: Universal Links are evaluated at step 1, not step 3. Since <code>track.esp.com<\/code> is not associated with your app, the app does not open. The user sees your website instead.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Who Wraps Links?<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Service<\/th>\n<th>Wrapping Domain<\/th>\n<th>Purpose<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Mailchimp<\/td>\n<td><code>track.mailchimp.com<\/code><\/td>\n<td>Click tracking<\/td>\n<\/tr>\n<tr>\n<td>SendGrid<\/td>\n<td><code>u12345.ct.sendgrid.net<\/code><\/td>\n<td>Click tracking<\/td>\n<\/tr>\n<tr>\n<td>HubSpot<\/td>\n<td><code>track.hubspot.com<\/code><\/td>\n<td>Click tracking, attribution<\/td>\n<\/tr>\n<tr>\n<td>Salesforce MC<\/td>\n<td><code>click.em.example.com<\/code><\/td>\n<td>Click tracking<\/td>\n<\/tr>\n<tr>\n<td>Braze<\/td>\n<td><code>ablink.example.com<\/code><\/td>\n<td>Click tracking<\/td>\n<\/tr>\n<tr>\n<td>Bitly<\/td>\n<td><code>bit.ly<\/code><\/td>\n<td>URL shortening<\/td>\n<\/tr>\n<tr>\n<td>Facebook<\/td>\n<td><code>l.facebook.com<\/code><\/td>\n<td>Link preview, safety check<\/td>\n<\/tr>\n<tr>\n<td>Instagram<\/td>\n<td>N\/A (in-app browser)<\/td>\n<td>Opens in WebView<\/td>\n<\/tr>\n<tr>\n<td>Twitter\/X<\/td>\n<td><code>t.co<\/code><\/td>\n<td>URL shortening, safety check<\/td>\n<\/tr>\n<tr>\n<td>LinkedIn<\/td>\n<td><code>lnkd.in<\/code><\/td>\n<td>URL shortening<\/td>\n<\/tr>\n<tr>\n<td>Slack<\/td>\n<td>(preview fetch only)<\/td>\n<td>Does not wrap user-visible link<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Impact on Each Platform<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">iOS (Universal Links)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Universal Links check the domain of the URL the user taps. If the tapped URL domain does not match the app&#39;s <a href=\"https:\/\/developer.apple.com\/documentation\/xcode\/allowing-apps-and-websites-to-link-to-your-content\/\" rel=\"nofollow noopener\" target=\"_blank\">AASA file<\/a>, the link opens in the browser.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Key behavior:<\/strong> iOS evaluates the initial URL, not the redirect destination. Even if the redirect lands on your Universal Links domain, it is too late.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Android (App Links)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Android <a href=\"https:\/\/developer.android.com\/training\/app-links\" rel=\"nofollow noopener\" target=\"_blank\">App Links<\/a> behave similarly: the app intercepts the URL only if the domain is verified. However, Android is slightly more forgiving:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Chrome on Android may follow 302 redirects and check the final URL.<\/li>\n<li>Intent filters can match a broader set of URLs.<\/li>\n<li>Some OEMs show a disambiguation dialog that includes the app even for redirected URLs.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Custom URL Schemes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Custom URL schemes (<code>myapp:\/\/...<\/code>) bypass the wrapping problem because they are not HTTP URLs. However, most ESPs and social platforms do not support custom URL schemes in links.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Solutions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Solution 1: Custom Tracking Domain<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Configure your ESP to use a subdomain you control:<\/p>\n\n\n\n<pre><code>Default:  https:\/\/track.mailchimp.com\/click?...\nCustom:   https:\/\/links.yourdomain.com\/click?...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then configure AASA and assetlinks.json on <code>links.yourdomain.com<\/code> to associate it with your app.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>ESP<\/th>\n<th>Custom Domain Support<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>SendGrid<\/td>\n<td><a href=\"https:\/\/docs.sendgrid.com\/ui\/account-and-settings\/how-to-set-up-link-branding\" rel=\"nofollow noopener\" target=\"_blank\">Branded links<\/a><\/td>\n<\/tr>\n<tr>\n<td>Mailchimp<\/td>\n<td>Custom domains for tracking<\/td>\n<\/tr>\n<tr>\n<td>Braze<\/td>\n<td><a href=\"https:\/\/www.braze.com\/docs\/user_guide\/message_building_by_channel\/email\/templates\/link_aliasing\/\" rel=\"nofollow noopener\" target=\"_blank\">Custom link domains<\/a><\/td>\n<\/tr>\n<tr>\n<td>HubSpot<\/td>\n<td>Custom tracking domains<\/td>\n<\/tr>\n<tr>\n<td>Salesforce MC<\/td>\n<td>Authenticated sending domains<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The ESP still wraps the link, but since the wrapped URL uses your domain, Universal Links activate on the wrapped URL itself.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Important:<\/strong> The AASA file must be served from the tracking subdomain, not just the main domain. If your ESP routes <code>links.yourdomain.com<\/code> to their servers, you need to configure the ESP to serve your AASA file on that domain (or use a CDN that serves both the ESP redirect and the AASA file).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Solution 2: Disable Click Tracking<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Some ESPs let you disable click tracking for specific links:<\/p>\n\n\n\n<pre><code class=\"language-html\">&lt;!-- SendGrid: Disable tracking for this link --&gt;\n&lt;a href=&quot;https:\/\/app.example.com\/products\/123&quot; clicktracking=&quot;off&quot;&gt;\n  View Product\n&lt;\/a&gt;\n\n&lt;!-- Mailchimp: Use mc:disable-tracking --&gt;\n&lt;a href=&quot;https:\/\/app.example.com\/products\/123&quot; mc:disable-tracking&gt;\n  View Product\n&lt;\/a&gt;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Trade-off: you lose click tracking data for those links. Use this only for deep links where app opens are more valuable than click data.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Solution 3: Redirect-Based Approach (Android Only)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For Android, you can rely on the redirect chain working:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>User taps <code>https:\/\/track.esp.com\/click?url=...<\/code><\/li>\n<li>ESP redirects to <code>https:\/\/app.example.com\/products\/123<\/code><\/li>\n<li>Chrome checks App Links on the redirect target.<\/li>\n<li>App opens.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">This does <strong>not<\/strong> work reliably on iOS because Safari does not activate Universal Links on redirects.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Solution 4: Two-URL Strategy<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use different URLs for iOS and Android:<\/p>\n\n\n\n<pre><code class=\"language-html\">&lt;!-- Email template with conditional deep links --&gt;\n&lt;!--[if iOS]--&gt;\n  &lt;a href=&quot;https:\/\/links.yourdomain.com\/products\/123&quot;&gt;View Product&lt;\/a&gt;\n&lt;!--[endif]--&gt;\n&lt;!--[if Android]--&gt;\n  &lt;a href=&quot;https:\/\/track.esp.com\/click?url=https:\/\/app.example.com\/products\/123&quot;&gt;View Product&lt;\/a&gt;\n&lt;!--[endif]--&gt;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In practice, email clients do not support conditional rendering by OS. Instead, use a single link that handles both platforms:<\/p>\n\n\n\n<pre><code class=\"language-typescript\">function buildEmailDeepLink(route: string): string {\n  \/\/ Use your custom tracking domain (works for both platforms)\n  return `https:\/\/links.yourdomain.com${route}`;\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Social Media Link Wrapping<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Facebook and Instagram<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Facebook wraps links with <code>l.facebook.com<\/code> and opens them in its in-app browser. Instagram opens all links in its in-app browser. Neither supports Universal Links.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Solution:<\/strong> Use a bounce page:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Link goes to <code>https:\/\/links.yourdomain.com\/bounce?r=\/products\/123<\/code><\/li>\n<li>Bounce page detects the in-app browser.<\/li>\n<li>Shows a button: &quot;Open in App&quot;<\/li>\n<li>Button opens the same URL in the system browser (using a JavaScript workaround), which triggers the Universal Link.<\/li>\n<\/ol>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ Bounce page: detect in-app browser and break out\nfunction openInSystemBrowser(url) {\n  \/\/ Safari on iOS: use window.open\n  if (\/iPhone|iPad\/.test(navigator.userAgent)) {\n    window.open(url, &#39;_blank&#39;);\n    return;\n  }\n  \/\/ Android: use Intent URL\n  if (\/Android\/.test(navigator.userAgent)) {\n    window.location.href = `intent:\/\/${new URL(url).host}${new URL(url).pathname}#Intent;scheme=https;end`;\n    return;\n  }\n  \/\/ Fallback\n  window.location.href = url;\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Twitter\/X<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Twitter wraps all links with <code>t.co<\/code>. The <code>t.co<\/code> redirect happens server-side, so the user&#39;s browser receives a 301 redirect to the original URL. On Android, this usually works (Chrome follows the redirect). On iOS, it sometimes works if the redirect is fast enough, but it is unreliable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Solution:<\/strong> Accept that Twitter links may open the web fallback for some iOS users. Ensure the web fallback has a clear &quot;Open in App&quot; button.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">URL Shorteners<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">The Problem<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">URL shorteners (Bitly, Rebrandly, TinyURL) are redirects. They suffer from the same issues as ESP link wrapping.<\/p>\n\n\n\n<pre><code>Short URL:    https:\/\/bit.ly\/abc123\nRedirects to: https:\/\/app.example.com\/products\/123\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">The Solution<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use your own link shortening domain with AASA and assetlinks.json:<\/p>\n\n\n\n<pre><code>Your short URL:  https:\/\/l.yourdomain.com\/abc123\nResolves to:     https:\/\/app.example.com\/products\/123\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Configure <code>l.yourdomain.com<\/code> as a Universal Links domain. When the user taps the short URL, iOS checks <code>l.yourdomain.com<\/code> for AASA and opens the app. No redirect needed because the short URL domain itself is associated with your app.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testing Link Wrapping<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Checklist<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Test<\/th>\n<th>Expected Result<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Send email with deep link via ESP, tap on iOS<\/td>\n<td>App opens (if using custom tracking domain)<\/td>\n<\/tr>\n<tr>\n<td>Send email with deep link via ESP, tap on Android<\/td>\n<td>App opens<\/td>\n<\/tr>\n<tr>\n<td>Share link on Facebook, tap in Facebook app<\/td>\n<td>Bounce page appears, &quot;Open in App&quot; works<\/td>\n<\/tr>\n<tr>\n<td>Share link via Twitter\/X, tap the t.co link<\/td>\n<td>App opens (Android) or web fallback (iOS)<\/td>\n<\/tr>\n<tr>\n<td>Use your short URL domain<\/td>\n<td>App opens on both platforms<\/td>\n<\/tr>\n<tr>\n<td>Use third-party shortener (bit.ly)<\/td>\n<td>Web fallback opens (expected)<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Tolinku for Link Wrapping<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/tolinku.com\/features\/deep-linking\">Tolinku<\/a> provides a dedicated link domain with AASA and assetlinks.json pre-configured, solving the link wrapping problem for custom tracking domains. See the <a href=\"https:\/\/tolinku.com\/docs\/concepts\/deep-linking\/\">deep linking documentation<\/a> for setup instructions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For QR codes and short links, see <a href=\"https:\/\/tolinku.com\/blog\/qr-codes-short-links-mobile-apps\/\">QR codes and short links for mobile apps<\/a>. For troubleshooting, see <a href=\"https:\/\/tolinku.com\/blog\/deep-linking-challenges\/\">common deep linking challenges and how to solve them<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Understand how link wrapping and redirects affect deep linking. Learn to handle email link tracking, social media wrappers, and URL shorteners.<\/p>\n","protected":false},"author":2,"featured_media":1829,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Link Wrapping and Redirects: Impact on Deep Links","rank_math_description":"Understand how link wrapping and redirects affect deep linking. Learn to handle email link tracking, social media wrappers, and URL shorteners.","rank_math_focus_keyword":"link wrapping deep links","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-link-wrapping-and-redirects.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-link-wrapping-and-redirects.png","footnotes":""},"categories":[11],"tags":[23,20,82,557,88,150,87,22],"class_list":["post-1830","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-deep-linking","tag-app-links","tag-deep-linking","tag-email-marketing","tag-link-tracking","tag-redirects","tag-social-media","tag-troubleshooting","tag-universal-links"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1830","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=1830"}],"version-history":[{"count":1,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1830\/revisions"}],"predecessor-version":[{"id":1831,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1830\/revisions\/1831"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/1829"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=1830"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=1830"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=1830"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}