QR QR Studio

howto

Deep links and QR codes: Universal Links, App Links and what replaced Firebase Dynamic Links

Published 7 September 2026 · 5 min read

A QR code that opens your app is one of the best uses of a code, and one of the most often broken. The camera only understands web links, the two phone platforms handle them differently, and a well-known shortcut for all of this closed in 2025. This guide walks through what to encode, how to make it open the app, and what to do when the app is not installed.

A deep link is a link that opens a specific screen inside an app rather than the app’s home screen or a web page. Scan a code on a product and land on that product in the app. Scan a code on a ticket and open that ticket. The alternative, sending everyone to the App Store listing, loses the context they scanned for. The app store type page covers the plain store link for when that is all you need.

Do this

A code on a shelf label that opens the product's page in the app if installed, or the same product on the website if not.

Not this

A code that opens the App Store, leaving the shopper to find the product again after installing.

Apps used to register custom schemes like myapp://product/42. They still work when tapped inside an app, but a phone camera is not an app that opens arbitrary schemes. iOS Camera and most Android scanners only act on http and https links. Encode a real web address on your own domain, such as https://example.org/p/42. That link must also be a working web page, because it is the fallback for everyone else.

Do this

https://example.org/p/42, which is a real page and also a claimed deep link.

Not this

myapp://product/42, which the camera shows as text and cannot open.

Worked example: a code for a recipe. Encode https://example.org/r/lemon-tart. On a phone with the app, it opens the recipe screen in the app. On any other device, it shows the recipe web page.

Universal Links are Apple’s way of letting an app own https links on a domain. Two pieces are needed. Your app declares the domain under Associated Domains in its entitlements. Your web server publishes a small JSON file called apple-app-site-association at https://example.org/.well-known/apple-app-site-association, listing the app and which paths it handles. When iOS sees a link on that domain, it checks the file and, if the app is installed, hands the link to the app. The Camera app then offers to open the link in your app instead of Safari.

Do this

The association file served over https with the correct content type, listing /p/* and /r/* as app paths.

Not this

The file only on the staging server, so production links open Safari every time.

App Links are the Android equivalent. The app declares intent filters for the domain with autoVerify enabled, and the server publishes https://example.org/.well-known/assetlinks.json listing the app’s package name and signing certificate fingerprint. Android verifies the file at install time. If it checks out, links on that domain open the app without asking. If not, the user gets a chooser or the browser.

Do this

assetlinks.json listing the release signing certificate, verified on a fresh install.

Not this

assetlinks.json listing only the debug certificate, so production installs never verify and links open Chrome.

Step 4: make the web page a real fallback

Most people who scan will not have the app. A tablet, a laptop camera, a friend’s phone, a new customer. The web page at the link must show them the same content and, if you like, a way to get the app. Do not make the fallback a blank page with a store button. That is the dead code problem in a new coat.

Do this

The recipe on the web, with a small Get the app banner at the top.

Not this

A page that says Download the app to continue and nothing else.

Step 5: decide whether you need deferred deep linking

Deferred deep linking handles the person who scans, does not have the app, installs it, and then expects to land on the thing they scanned. The store visit breaks the chain, so something has to remember the link for them. That is normally a deep-linking service that records the click, matches the first app launch to it, and passes the original link into the app.

It is worth having when the code leads to a specific offer, ticket or referral where losing context costs the conversion. It is not worth the dependency for a code that opens a general product page.

Do this

Deferred deep linking on a referral code, so the new user's first launch shows the referrer's offer.

Not this

Deferred deep linking on a code that opens the app's home screen, adding a service for no benefit.

Firebase Dynamic Links was a free Google service that did all of this: one short link that opened the app if installed, the store if not, and remembered the deep link across install. Google shut it down on 25 August 2025, and links stopped resolving. Any QR code printed with a page.link address, or a custom Dynamic Links domain, now leads nowhere unless the owner set up their own redirect.

If you have codes from that era, check them. If you are starting now, the options in general terms are:

  • Your own domain with Universal Links, App Links and a web fallback. No third party, no shutdown risk, but no deferred deep linking unless you build it.
  • A deep-linking service from a vendor that offers hosted links, deferred deep linking and analytics. Convenient, but the link lives on their domain or a domain they manage, so read the terms and plan for the day they change.
  • Your own domain in the code, redirecting to a service behind it. The printed link is yours, so a change of vendor is a server change, not a reprint.
Feature Own domain, Universal and App Links Deep-linking service Custom scheme
Opens from the camera app
Opens the app when installed Only from inside another app
Web fallback when not installed
Deferred deep linking after install Only if you build it
No third-party dependency
Printed link survives a vendor change Only if you own the domain Not applicable
The camera rules out custom schemes. Between the other two, the deciding questions are whether you need deferred deep linking and whether the printed domain is yours.

Common mistake

Printing a link on a vendor’s domain. When the vendor closes, is bought or changes its pricing, every printed code changes with it. Put your own domain in the code and redirect from there. A reprint is far more expensive than a DNS record.

Step 6: test on both platforms

Test four cases on each platform: app installed and not installed, and the link scanned from the camera and pasted into a browser. Check that iOS shows the Open in app banner, that Android opens the app without a chooser, and that a laptop lands on the web page. Then put the exported code through the decoder to confirm it holds exactly the link you meant, and follow the rest of the deployment checklist.

Do this

Tested installed and not installed on an iPhone and an Android phone, from the camera and a browser, before printing.

Not this

Tested once on the developer's phone with the debug build installed.

Try it in QR Studio

QR Studio’s dynamic codes put a short link on a stable domain in the printed code and let you change where it goes later, so a change of deep-linking vendor or a new app version never means reprinting.

Checklist

  • Code holds an https link on your own domain, not a custom scheme
  • The link is a working web page with the same content as the app screen
  • apple-app-site-association served at /.well-known/ over https on production
  • assetlinks.json served at /.well-known/ with the release certificate fingerprint
  • Web fallback shows the content, not only a store button
  • Deferred deep linking used only where losing context costs a conversion
  • No printed links on a vendor’s domain, or a redirect from your domain in front of it
  • Old Firebase Dynamic Links codes found and redirected or replaced
  • Tested installed and not installed on iOS and Android, from camera and browser
  • Exported code decoded to confirm the exact link

Try it yourself

Drop the exported code in the decoder. If it shows a custom scheme or a page.link address, fix the link before anything is printed.

Decode your app code

Frequently asked questions

Can a QR code open an app directly?

Yes, if the code holds an https link on a domain that your app has claimed with Universal Links on iOS and App Links on Android. If the app is not installed, the same link opens the web page.

Why does my myapp:// QR code not work?

Phone cameras only open http and https links. A custom scheme is not a web address, so the camera either ignores it or shows the raw text. Use an https link and claim it with Universal Links and App Links.

What is deferred deep linking?

Taking a person who did not have the app to the store, then opening the right screen after they install it. It needs a service to remember the link between the store visit and the first launch.

What happened to Firebase Dynamic Links?

Google shut the service down on 25 August 2025. Existing links stopped resolving. Replacements are your own domain with Universal Links and App Links plus a fallback page, or a third-party deep-linking service.

Do I need a different QR code for iPhone and Android?

No. One https link works for both. The phone decides what to do with it based on which app has claimed the domain.

Related reading

Make yours in QR Studio

Unlimited static codes free, no ads, no watermark. AI design, dynamic codes and print-ready export when you need them.

Download on the App Store