Deeplinkly
All articles
Deep LinkingMobile Development

URL Scheme vs Universal Links: Why Custom Schemes Break Deep Links

Published August 16, 2026·16 min read·By Sahil Asopa
Verified mobile links reaching app and web destinations while a custom route breaks

A campaign link works in your test build, then a customer taps it from an email and gets a dead page. Another app claims the same route. A new user installs your app but lands on the home screen with the campaign context gone. These are not three unrelated bugs; they are predictable limits of a custom URL scheme used outside the narrow job it was designed to do.

Custom schemes remain useful for controlled app-to-app handoffs and some native callbacks. They are a poor default for links that customers share, ads distribute, or new users open before installing. This guide explains the boundary, shows how verified HTTPS links differ, and gives app teams a practical migration and QA plan.

What is a URL scheme?

A URL scheme is the part before the first colon in a URI, such as https in https://example.com or myapp in myapp://product/42. A custom URL scheme lets a mobile app register a private prefix so the operating system can launch that app and pass it the full URI for routing.

The formal URI syntax is scheme:[//authority]path[?query][#fragment]; the IETF URI specification defines the scheme as the leading component. In mobile development, “URL scheme,” “URI scheme,” “custom scheme,” and “scheme URL” often refer to the same app-defined mechanism.

For example:

text
shopco://product/42?campaign=summer

The operating system does not fetch a web page at that address. It looks for an installed app that has declared the shopco scheme, launches a matching handler, and supplies the URI. Your app then parses the host, path, and query, validates them, and maps the result to an internal destination.

On iOS, an app declares schemes through CFBundleURLTypes and handles the incoming URL. Apple calls custom schemes an acceptable deep-link mechanism, but its custom URL scheme documentation strongly recommends Universal Links for links uniquely associated with a website.

On Android, a browsable activity declares an intent filter containing the expected scheme, host, and path. Android then resolves the intent among installed handlers. The Android deep-link guide distinguishes custom deep links, ordinary web links, and verified App Links because they have different routing and trust behavior.

The scheme is only an entry point. It does not provide deferred deep linking, attribution, fallback pages, access control, or a stable routing contract by itself.

Why a URL scheme breaks user-facing journeys

Custom schemes feel reliable during development because the test device has the right app installed and the link is opened from a cooperative source. A production campaign adds uncontrolled install state, browsers, in-app webviews, competing handlers, old app versions, and untrusted input.

There is no verified owner

Registering a reverse-domain-style value such as com.example.app reduces accidental collisions, but it does not prove ownership. Apple notes that another app can register the same scheme and that the target is undefined when multiple apps claim it. Android likewise warns that more than one app can match a deep-link intent.

That creates both reliability and security failures. A competing or malicious app can claim the same URI scheme, intercept an authorization response, or show a deceptive screen. Verified links solve the ownership question by requiring both the app and a domain you control to declare their relationship.

The missing-app path is a dead end

myapp://product/42 has meaning only when a compatible handler is installed. If the app is absent, a browser cannot render useful content at that URI. The user may see an error, nothing may happen, or the source may suppress the navigation.

An HTTPS link has a natural fallback: the same address can load a mobile web page. Apple’s Universal Links documentation says an installed app can open associated content directly, while a user without the app reaches the website in the browser.

The install gap drops route context

A URL scheme does not survive an App Store or Play Store installation. Sending a user to the store changes the journey, and the newly installed app does not automatically know which product, referral, or campaign preceded installation.

That is a deferred deep-linking problem, not a string-format problem. It requires a service to capture eligible click context, route to the correct store, match the first app open using supported signals, and restore the destination. The app should still handle an unmatched result explicitly instead of guessing; this deferred deep-link implementation guide maps the full install-to-route workflow.

Every caller treats schemes differently

Email clients, social apps, QR scanners, browsers, ad networks, and embedded webviews do not share one execution policy. Some allow custom schemes, some require a user gesture, and some block or rewrite them. Even a technically correct link can fail before the operating system receives it.

This is why a timer-based web redirect that tries a scheme and then jumps to an app store is fragile. It guesses whether the app opened based on elapsed time and page visibility, not a reliable installation signal. It can double-navigate, trigger warnings, or send an installed user to the store.

A successful open is not a successful route

Launching the app proves only that a handler ran. Routing can still fail because a path is unknown, a query value is malformed, authentication state blocks the destination, or a released app version does not support the route.

Treat every inbound URI as untrusted input. Apple’s guidance for both custom schemes and Universal Links says to validate parameters, discard malformed URLs, and prevent links from directly performing destructive or sensitive actions.

URL Scheme vs Universal Links and App Links

Universal Links are Apple’s verified HTTPS deep links. App Links are Android’s equivalent. Both let a standard web URL open matching content in an installed app after the operating system verifies a two-way association between the domain and app.

Decision factorCustom URL schemeiOS Universal LinkAndroid App Link
Exampleshopco://product/42https://go.shopco.com/product/42https://go.shopco.com/product/42
Ownership proofNoneAASA file plus app entitlementassetlinks.json plus verified intent filter
App not installedUsually failsOpens the web URLOpens the web URL
Another app can claim itYesNot after valid associationNot after valid association
Works as a normal web linkNoYesYes
Best fitControlled callbacks and app-to-app actionsUser-facing iOS linksUser-facing Android links
Deferred routing after installNot built inNot built inNot built in

On iOS, you host an apple-app-site-association file for the domain and add the associated-domains entitlement to the app. The system checks that both sides agree before handing a matching HTTPS link to the app.

On Android, you declare an http/https intent filter with android:autoVerify="true" and host /.well-known/assetlinks.json. The file identifies the Android package and signing-certificate fingerprint. Google’s website association guide also requires the file to be available over HTTPS without redirects.

The important difference is not “old link versus new link.” It is unverified device-local registration versus a verified app-domain relationship with a real web destination. For the broader terminology and platform comparison, see deep linking vs Universal Linking.

For acquisition links, Google Ads makes the same operational choice: its Web to App Connect guidance supports App Links on Android and Universal Links on iOS, while custom schemes are not supported because they are less secure and can fail when the app is absent.

When a URL scheme is still the right tool

A custom scheme is not obsolete. It is a specialized transport. Keep it when the caller and install state are controlled, the data is non-sensitive or separately protected, and an HTTPS resource would not improve the flow.

Good candidates include:

OAuth deserves care. RFC 8252, OAuth 2.0 for Native Apps, permits private-use scheme redirects but explains that multiple apps can register the same scheme and intercept an authorization code. Public native clients must use Proof Key for Code Exchange (PKCE), and reverse-domain-based schemes improve collision resistance. Also register and match the complete redirect URI; do not treat the scheme name alone as authorization.

Avoid custom schemes for email, SMS, paid media, public QR codes, referral links, shared product pages, and any route that may reach a person without the app. Those journeys need a browser destination and verified ownership.

Verified HTTPS routing to installed app, web fallback, and post-install continuation

How to migrate a URL scheme to verified links

Do not replace myapp:// strings one by one. Build a stable public link contract, then run the old and new transports in parallel long enough to migrate callers safely.

1. Inventory every route and caller

Search app code, web redirects, email templates, push payloads, QR assets, partner documentation, OAuth settings, and analytics rules. For each scheme URL, record:

Classify the route as public user-facing, controlled app-to-app, authentication callback, or internal-only. The category determines whether you migrate it, retain it with controls, or remove it.

2. Define one canonical destination model

Separate the business destination from the transport. A product destination might be represented internally as:

json
{
  "route": "product",
  "id": "42",
  "campaign": "summer"
}

Map both shopco://product/42?campaign=summer and https://go.shopco.com/product/42?campaign=summer into that validated object. One router should handle cold starts, warm starts, logged-out users, expired content, and minimum-version requirements.

Document parameter types and limits. Reject unknown privileged actions, normalize encoded values once, and never place session tokens, passwords, or unnecessary personal data in a link. If a destination requires authentication, preserve only a safe return path and complete the action after the user signs in.

3. Configure the iOS association

Serve the AASA JSON at https://your-domain/.well-known/apple-app-site-association and list the app identifiers and permitted components or paths. Add applinks:your-domain to Associated Domains in the signed app, then route the incoming NSUserActivity URL through the same canonical parser.

Scope paths deliberately. Associating every path on a large domain can send ordinary website journeys into the app. Keep a deliberate web escape for content that should remain in the browser, and ensure every associated path has an app destination or a safe in-app fallback.

4. Configure Android App Links

Add verified https intent filters with VIEW, DEFAULT, BROWSABLE, the host, and android:autoVerify="true". Publish assetlinks.json on every associated host with the production package name and the SHA-256 fingerprint used for the app users actually install.

Do not assume the local upload key matches Play App Signing. Google explicitly notes that Play-managed signing can use a different certificate; use the fingerprint shown in Play Console. On Android 15 and later, optional Dynamic App Links rules can refine paths and query matching from the server, but those rules cannot expand beyond the host scope declared in the manifest.

5. Build web and install fallbacks

Every public HTTPS link should return useful content when the app does not open. Choose per route: render the product on mobile web, explain that the content is app-only, offer the appropriate store, or provide a desktop-safe landing page.

If the post-install destination matters, add deferred routing rather than disguising an app-store redirect as a deep link. Capture only the context you need, set an eligibility window, make attribution precedence explicit, and distinguish matched, unmatched, and ambiguous installs.

This is the layer where a deep-linking platform can remove substantial operational work. Deeplinkly provides branded HTTPS links, deferred deep linking, install attribution, and cross-platform routing through lightweight SDKs, so a team can manage link behavior and measurement without maintaining every redirect and match component itself. A proof of concept should still be judged against your route matrix and privacy requirements.

6. Keep compatibility during rollout

Release app support before replacing external links. For a transition period, let both transports map into the same router. Update owned surfaces first, then partner integrations and long-lived assets such as QR codes.

Instrument the migration by transport and source. Track at least link received, app opened, destination resolved, fallback shown, first open matched, and target screen viewed. A rising app-open rate can hide a declining destination-success rate, so keep those events separate.

Google shut down Firebase Dynamic Links on August 25, 2025; its deprecation FAQ says hosted links stopped working and points teams toward direct App Links and Universal Links or another provider. If old page.link URLs still exist in campaigns or QR codes, inventory them as broken external dependencies, not as harmless legacy aliases.

7. Retire the old scheme carefully

Do not remove a public scheme handler while released callers still depend on it. First stop generating new scheme URLs, publish a cutoff schedule for partners, measure remaining opens by source, and leave a safe compatibility handler through the supported migration window.

When the old route arrives, accept only known paths and forward them internally. Do not chain it through a browser timer. Remove obsolete privileged actions before removing the scheme registration itself.

URL Scheme testing: a release matrix that catches real failures

Test the destination, transport, and measurement separately. A passing simulator command does not prove that an email tap, a production signing certificate, or a first install will behave correctly.

Use a matrix that covers:

VariableMinimum cases
PlatformCurrent and oldest supported iOS; representative Android versions including Android 12+
App stateNot installed, freshly installed, terminated, backgrounded, foregrounded
User stateSigned out, signed in, expired session, restricted account
SourceBrowser, email, messaging app, QR scanner, paid-media test, push notification
DestinationValid, unknown, deleted, permission-gated, version-gated
ParametersMissing, duplicated, encoded, oversized, malformed, unexpected
NetworkOnline, offline, slow, association endpoint unavailable
BuildDebug, internal distribution, production-signed store build

For iOS, verify the public AASA response, the signed entitlement, the app identifier, and the exact path rule. Test taps from representative external apps with a production-like install; do not rely only on pasting a URL into a browser address bar. If Safari opens instead, work through the iOS Universal Links debugging checklist.

For Android, confirm every host has the correct Digital Asset Links file and certificate fingerprint. Google documents manual verification, host checks, and device link policies in its App Links testing guide. Include both upgrade and clean-install tests because association state and app defaults can differ.

For each case, assert the full outcome:

  1. Did the source emit the expected URL?
  2. Did the operating system choose the intended app or browser?
  3. Did the app parse and authorize the route?
  4. Did the user reach the exact content?
  5. Did analytics record one consistent journey without duplicate opens?
  6. If the app was absent, did web or store fallback work?
  7. After installation, was eligible context restored or explicitly reported unmatched?

Automate route parsing and navigation tests in code, but retain a physical-device smoke suite for association, browser, store, and in-app-webview behavior.

Common URL scheme failures and the fastest diagnosis

The app opens to the home screen. The transport worked; the router did not. Log the sanitized inbound URI, parsed destination, authentication gate, and final route result. Test cold-start and warm-start handlers separately.

A verified HTTPS link opens the browser. Check the association file first, then entitlements or manifest, production identifiers, certificate fingerprints, redirects, content type, and path scope. If the website response is correct, inspect device verification state and test a clean install.

A custom scheme opens the wrong app. You have a scheme collision. A more distinctive name can reduce accidental overlap but cannot establish ownership. Move sensitive and user-facing flows to verified links.

The link works for existing users but not new users. That is expected without a web fallback and deferred routing. Separate the no-app landing experience from the post-install context-recovery mechanism.

Clicks are measured but target-screen views fall. Do not optimize on link opens alone. Segment route success by source, OS, app version, installed state, and destination type; the failure is likely after the click collector but before content render.

Frequently asked questions

What is the difference between a URL and a URI scheme?

A URI scheme identifies how a resource reference should be interpreted; it is the first component before the colon. A URL is a URI that identifies a resource by its location and access mechanism. In everyday mobile documentation, “URL scheme” and “URI scheme” are often used interchangeably for app-defined links such as myapp://item/42.

Are custom URL schemes secure?

Not by ownership alone. Another app can register the same custom scheme, so schemes are vulnerable to collision and interception. Use verified HTTPS links for user-facing or sensitive journeys; when an OAuth flow must use a private-use scheme, use PKCE, an exact registered redirect URI, a reverse-domain scheme, and strict parameter validation.

Do Universal Links replace URL schemes?

They replace custom schemes as the preferred transport for public, user-facing iOS links. They do not eliminate every scheme use case: controlled app-to-app communication, legacy SDK callbacks, and some native authentication flows may still require one.

What happens when a URL scheme is opened without the app installed?

There is no standard web resource to open, so the navigation usually fails or shows an error. A verified HTTPS link can open the corresponding website instead, and a separate deferred deep-link system can restore eligible context after installation.

Can the same HTTPS URL work on iOS, Android, and the web?

Yes. Associate the domain with the iOS app through AASA and with the Android app through Digital Asset Links, while serving a real web response at the URL. Platform rules and app routing still need separate configuration and testing.

Should an app support both verified links and a custom URL scheme?

Often, yes, but for different jobs. Use Universal Links and App Links for public journeys, and retain a custom scheme only for documented controlled flows that cannot use HTTPS. Map both into one validated internal router instead of maintaining two destination systems.

Conclusion

Choose the link transport from the user journey, not from setup speed. If a link can leave your controlled environment, reach someone without the app, carry sensitive state, or drive paid acquisition, use a verified HTTPS URL with a useful web fallback. Add deferred routing only when the destination must survive installation.

Keep a custom URL scheme for the small set of callbacks and app-to-app actions where it remains appropriate, then constrain and test it as an unverified input channel. To decide whether to build the operational layer or use a provider, run one production-like route matrix and compare destination success, post-install recovery, security controls, and measurement quality—not just whether the app opens.

Back to all articles© 2026 Deeplinkly

Related guides