Glossary/Failure modes
Deep links in email clients
Definition
Deep links break in email because click tracking rewrites the destination URL onto the email provider's domain, and neither iOS nor Android resolves the redirect before deciding which app may handle the tap.
This is the failure that survives every other fix. The association file is correct, the entitlement is present, the link works when pasted into Notes — and it fails from the campaign, because the URL in the campaign is not your URL. It has one root cause and one real fix, and the fix is infrastructure rather than app configuration.
What click tracking does to your link
Every email platform rewrites outbound links so it can count clicks. What you author and what the recipient taps are different URLs on different domains.
# What you put in the campaign
https://example.com/products/42
# What actually ships in the email
https://click.sendgrid.net/ls/click?upn=aHR0cHM6Ly9leGFtcGxl...
# What the OS is asked to resolve when the user taps: click.sendgrid.net.
# That domain is not in your apple-app-site-association file, so no app
# claims it, so it opens in the browser. The 302 to example.com happens
# afterwards — inside the browser, where the OS no longer asks anything.The decisive detail is the ordering. Both platforms evaluate the URL being navigated to, not the one it eventually resolves to. By the time the redirect to your domain happens, a browser is already open and the routing decision has been made. A redirect can never rescue a deep link; it can only follow one.
| Source | What it does | Under whose control |
|---|---|---|
| Email platform click tracking | Rewrites every link onto their click domain | Yours — usually a per-campaign or per-link setting |
| Microsoft Defender Safe Links | Rewrites links onto *.safelinks.protection.outlook.com | The recipient's IT department |
| Corporate email security gateways | Rewrite for scanning, similar to Safe Links | The recipient's IT department |
| Link shorteners | Add a hop on a domain you do not own | Yours — stop using them for app links |
| Ad network click servers | Same mechanism, same failure | Partly yours, via the tracking template |
Two of those five you cannot fix
Safe Links and corporate gateways rewrite links after the message leaves you, on domains you will never control. For recipients behind them, deep links from email cannot work, and the destination web page is the entire experience. That is a reason to invest in the landing page, not a reason to keep debugging.
The fix: a tracking domain that is also your domain
Every major email platform supports a custom tracking domain — click tracking that runs on a subdomain of yours rather than theirs. Once the tap URL is on a host you own, you can put that host in your association files, and the deep link resolves before the redirect matters.
# CNAME the tracking subdomain at your provider
links.example.com. CNAME sendgrid.net.
# Now the URL in the email is on a host you own
https://links.example.com/ls/click?upn=aHR0cHM6...
# Serve the association files from THAT host too
https://links.example.com/.well-known/apple-app-site-association
https://links.example.com/.well-known/assetlinks.json<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:example.com</string>
<string>applinks:www.example.com</string>
<!-- The host the email platform actually sends people to -->
<string>applinks:links.example.com</string>
</array>The corresponding Android change is adding links.example.com as a <data android:host> in the autoVerify intent filter, and serving assetlinks.json from it. Both files must satisfy every hosting rule on the tracking host independently — and tracking hosts are often behind a provider's infrastructure that redirects or refuses /.well-known/ paths, so verify rather than assume.
Check that the CNAME target will serve your file
A CNAMEd tracking domain resolves to the provider's servers, which means *they* answer requests for /.well-known/apple-app-site-association — not you. Some providers host the file for you if you upload it, some let paths through to an origin, and some do neither. This is the single question to ask before committing to a provider for deep-linked email.
Client-by-client behaviour
With the tracking problem solved, the client still has to hand the tap to the OS. Most do; the ones that do not fail for the same reason in-app browsers do.
| Client | Behaviour | Deep link opens the app? |
|---|---|---|
| Apple Mail (iOS) | Hands off to the default browser | Yes |
| Gmail app (iOS) | Default browser, or its own webview depending on settings | Usually — verify with your own account |
| Gmail app (Android) | Hands off to the system | Yes |
| Outlook mobile | Hands off, but Safe Links may have rewritten the URL | Only without Safe Links |
| Spark, Superhuman, Hey | Generally hand off to the system | Yes |
| Gmail, Outlook, or any client in a desktop browser | Opens a desktop browser | n/a — no app to open |
Note the last row. A large share of email is read on desktop, where there is no app to open and the web page is the whole experience. This is the same conclusion the in-app browser problem reaches from a different direction: the destination page carries more of your email programme than the deep link does.
Testing it honestly
A hand-written email is not a test. It has no click tracking, so it exercises the one path that was never broken, and it passes on setups that fail for every real recipient.
- Send a real campaign through your actual platform to a test list, with tracking enabled exactly as production has it.
- On the device, long-press the link before tapping and read the URL. If the host is not one of yours, nothing else on this page matters yet.
- Tap it. Confirm the app opens rather than the browser.
- Repeat on an Outlook account inside an organisation with Safe Links, so you see what that population gets.
- Confirm the destination page stands on its own, because desktop readers and Safe Links recipients will only ever see it.
# Copy the link out of a received test email, then follow the chain.
# Every hop before your domain is a hop where the app cannot be offered.
curl -sSIL "https://click.sendgrid.net/ls/click?upn=..." \
| grep -Ei '^(HTTP/|location:)'
# HTTP/2 302
# location: https://example.com/products/42
# HTTP/2 200
#
# Two hops. The tap was to click.sendgrid.net; example.com never got the
# chance to claim it.deep link debugger
Run it against your tracking subdomain, not just your main domain. That host is the one users actually tap, and it is the one most likely to be missing its association files — the debugger checks whether the provider's infrastructure serves them correctly from /.well-known/.
Frequently asked questions
- Why do my deep links not work from email campaigns?
- Because your email platform rewrites every link for click tracking, so the URL the recipient taps is on the platform's domain rather than yours. Neither iOS nor Android resolves the redirect before deciding which app may handle a tap, so the OS sees a link to the tracking domain, finds no app claiming it, and opens the browser. The redirect to your domain happens afterwards, inside the browser, too late to matter.
- How do I make deep links work in email?
- Configure a custom tracking domain with your email platform, so click tracking runs on a subdomain of yours such as links.example.com. Then serve apple-app-site-association and assetlinks.json from that host, add it to your Associated Domains entitlement, and add it as a data host in your Android intent filter. Once the tapped URL is on a host you own and have associated, the app opens normally.
- Does Microsoft Safe Links break deep links?
- Yes, and you cannot fix it. Safe Links rewrites URLs onto safelinks.protection.outlook.com, a domain controlled by Microsoft and configured by the recipient's IT department. For recipients behind it, the tap resolves to Microsoft's domain and your app is never offered the link. The destination web page is the entire experience for that population.
- Can I just turn off click tracking for links that should open the app?
- Most platforms allow disabling tracking per link or per campaign, and it does fix the deep link. The trade-off is losing click data for exactly the links you most want to measure. A custom tracking domain is the better answer because it keeps both, and it is a one-time DNS and hosting change rather than a per-campaign discipline.
- Why does my deep link work when I email it to myself but not in a campaign?
- A hand-written email contains your original URL with no click tracking, so it tests the only path that was never broken. A campaign sent through your platform rewrites the link onto the tracking domain. Always test with a real campaign, tracking enabled, and long-press the link on the device to check which host it actually points at before you tap it.
- Do I need to host assetlinks.json on my tracking domain?
- Yes, if you want App Links to work from email. Every host that users tap through needs its own association files, served from that exact host. This is worth checking before committing to a provider, because a CNAMEd tracking domain resolves to the provider's servers, so they answer requests for the .well-known path rather than you — and not all of them will serve your file.
Related terms
- Deep links in in-app browsers — An in-app browser is an embedded webview inside another app that renders links without handing them to the operating system, which prevents Universal Links and App Links from ever reaching the app that claims the domain.
- Deep link opens the browser instead of the app — A deep link opens the browser instead of the app when the operating system has not accepted the app as a verified handler for that URL, or when the tap occurred in a context that never offers the app the chance to handle it.
- Testing a deep link — Testing a deep link means verifying three separate things — that the operating system routes the URL to the app, that the app receives it, and that the app navigates to the right screen — each of which can pass while the others fail.