QR codes for app downloads — App Store and Google Play

A QR code for app downloads should send iPhone users to the App Store and Android users to Google Play, with zero dead ends. Here's how to wire it.

May 24, 2026 18 min read Linked.Codes
QR codes for app downloads — App Store and Google Play

A QR code for app downloads has exactly one job: scan it on iPhone, land in the App Store on your app's page. Scan it on Android, land in Google Play on the same app. Scan it on a desktop browser by accident, land on a webpage that explains the app and offers both store buttons. Most QR codes printed for app downloads fail at one of those three branches, and the failure mode is silent — the user shrugs and walks away. There is no install, no error, no support ticket. You just lose the user.

This post is about how to wire a QR code that doesn't lose the user. It covers the right way to detect platform, the deep-link traps that look fine in QA and break in production, the difference between a Universal Link and a Custom URL Scheme on iOS (and the equivalent on Android), and the small set of edits that turn a 60% install-conversion QR into a 90% one. The numbers in this post come from Apple's own developer guidance, Google Play Console data shared at I/O sessions, and a handful of agency case studies cited at the end.

The default QR code people print is the wrong one

The most common pattern: someone goes to the App Store on their phone, copies the app's URL (https://apps.apple.com/us/app/example/id123456), pastes it into a free QR generator, prints the result. It works on iPhone. It fails on Android because Android's QR scanner opens an App Store link in a web browser, which then sits there showing the iOS app page — and offering no path to Google Play.

The right pattern is a single QR code that encodes a short URL on your own redirect layer. That URL inspects the scanning device's User-Agent header server-side and returns a 302 redirect to the correct destination. iOS gets apps.apple.com, Android gets play.google.com, anything else gets a fallback landing page. One QR. Three branches. Zero dead ends. The App Store QR code generator wires that three-way split as a single short-link target — paste both store URLs plus a web fallback, get back a QR whose redirect layer already handles iPhone, Android, and desktop scanners differently without a custom script.

This is the same architecture as a device-targeted short link, applied to the specific case of app downloads. The short link absorbs all the platform-detection complexity. The QR code stays sparse, brandable, and reprintable.

QR code for app downloads — three-way redirect flow One QR, three branches, no dead ends QR scan lnks.work/k/app redirect layer User-Agent inspection iOS → App Store Android → Google Play Desktop → landing page The hard part isn't the redirect. It's the User-Agent edge cases — in-app browsers, iPad in desktop mode, Huawei devices without Play Services, Android Auto, smart fridges, and the cohort of devices that report "Mobile Safari" while running on a stripped-down WebView. The next two thousand words are about those edge cases.
The architecture is simple. The trap is that User-Agent strings are messy in 2026 and the obvious detection logic breaks on roughly one in twenty real scans.

How User-Agent detection actually works in 2026

The server sees an HTTP request with a User-Agent header. That header is a free-text string the browser claims to be. iOS Safari sends something like Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1. Android Chrome sends Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36. The detection rule looks obvious: search the string for "iPhone" or "iPad" or "iPod" — that's iOS. Search for "Android" — that's Android. Otherwise, desktop.

That rule is right about 95% of the time. The remaining 5% is where installs are lost. Five edge cases worth knowing:

iPad in desktop mode. Since iPadOS 13, iPad's Safari defaults to requesting desktop sites. The User-Agent claims to be macOS Safari, not iOS Safari. The string contains "Macintosh" and "Mac OS X", no "iPad". The fix is checking for navigator.maxTouchPoints > 1 client-side, or matching the User-Agent against (iPad|Macintosh.*Mobile) server-side. Apple documents this behaviour but doesn't loudly advertise it. Detecting iPad reliably from headers alone is hard; the client-side touch check is the pragmatic workaround.

In-app browsers. Someone scans your QR from inside Instagram. Instagram opens the link in its own WebView. The User-Agent contains Instagram/267.0.0, not the platform's default browser string. Same for TikTok, Twitter/X, LinkedIn, WeChat, and a long tail of others. The good news: the WebView still carries the underlying platform's string ("iPhone" or "Android"), so platform detection works. The bad news: opening an apps.apple.com URL inside Instagram's WebView shows the page but the "Open in App Store" button often doesn't work because the WebView blocks the deep-link. Detect in-app browsers and prefix the redirect with ?ct=instagram so analytics catch the case, and consider sending a JS that calls window.open(url, '_system') to escape the WebView on platforms that respect it.

Huawei without Play Services. Huawei devices sold after 2019 ship without Google Play. The User-Agent says "Android" but Google Play URLs return 404. If your app is on AppGallery, the redirect should detect Huawei (User-Agent contains HuaweiBrowser or HMSCore) and route to your AppGallery listing instead. If your app isn't on AppGallery, send the user to a landing page that explains why and offers the APK or web app.

WebViews on Android (Chrome Custom Tab). Many Android apps open links in a Chrome Custom Tab, which uses the system Chrome. The User-Agent is the standard Chrome string, no in-app marker. The detection works, the redirect works, the Play Store deep-link works. This is the case that's usually fine — note it because it's the case people worry about and don't need to.

Smart fridges, cars, kiosks. Reporting User-Agent strings that vaguely resemble Android but don't have Play Store. Rare for app-download QR codes specifically, but if your QR ends up on a billboard scanned by a Tesla browser, the redirect should route to your web landing page. Default-to-landing on anything that isn't a confidently-detected phone is the safe rule.

The App Store URL pattern is the simple case. The harder case is when your app is already installed and you want the QR to deep-link into a specific screen — say, "open the app and show this product page" rather than "open the App Store."

iOS has two mechanisms for this and one of them is a trap.

Custom URL Schemes (the trap): a scheme like myapp://product/123. The QR encodes the scheme URL. When scanned, iOS checks whether any installed app has registered myapp:// as a scheme and opens it. If no app has registered it — most commonly because the app isn't installed yet — the URL fails silently. No fallback, no App Store prompt, no error. The user sees nothing happen.

This is the single most common reason app-download QR codes fail in production. Someone read a 2018 blog post about deep-linking, encoded a myapp:// scheme into the QR, the engineering team tested with the app already installed, it worked, they shipped. Real users without the app installed get a dead QR.

Universal Links (the right way): a regular https:// URL that your app has registered as one it can handle. The QR encodes https://yourapp.com/product/123. When scanned, iOS checks whether your app is installed and registered for that URL pattern. If yes, the app opens. If no, Safari opens the URL like any normal link — which means your server controls the fallback. Serve a page that detects "no app installed" and redirects to the App Store. Result: deep-link works when app is installed, App Store fallback works when it isn't.

Universal Links require an apple-app-site-association file on your domain. It's a JSON file at https://yourapp.com/.well-known/apple-app-site-association that lists which paths your app claims. Apple's Universal Links documentation covers the setup. The file has to be served over HTTPS with the correct content-type. Skipping the content-type is the most common bug; Apple's verifier won't accept text/html for the association file.

Android's equivalent is App Links — same idea, regular https:// URLs claimed by your app via a assetlinks.json file at https://yourapp.com/.well-known/assetlinks.json. Google's App Links documentation describes the verification flow. The trap is the same: the older intent:// URL scheme has analogous "fails silently if app not installed" behaviour. Don't use it. Use App Links.

The net rule: encode a normal https:// URL into the QR, never a custom scheme. The web layer handles "app installed" vs "app not installed" vs "no platform support". The QR doesn't have to know.

A working app-download URL pattern

Here's the redirect logic in plain English. Your short link lnks.work/k/app (or your branded domain equivalent) inspects the request and returns:

if User-Agent matches iPad-in-desktop-mode OR contains "iPhone|iPad|iPod":
    redirect to https://apps.apple.com/app/idXXXXXXXXX
elif User-Agent contains "Huawei|HMSCore" AND app is on AppGallery:
    redirect to https://appgallery.huawei.com/app/CXXXXXXX
elif User-Agent contains "Android":
    redirect to https://play.google.com/store/apps/details?id=com.yourapp
else:
    redirect to https://yourapp.com/download

The https://yourapp.com/download landing page is the catch-all. It should be a simple page with two App Store buttons, a screenshot of the app, and a short paragraph about what the app does. People who scan from a desktop browser, from a smart TV, from a Linux laptop, or from any device the User-Agent check didn't classify as a phone will land here. They might forward the URL to themselves on their phone. They might click one of the buttons after going to the device they meant to install on. Either way: no dead end.

This is a QR-codes post, so the QR side matters separately from the redirect logic. Three things change when the destination is an app-download flow:

Density. The QR encodes a short URL, not the full App Store URL. The short URL is 17–22 characters; the full App Store URL is 50–60. The density comparison between URL QR codes and short-link QR codes is the foundation — for an app QR specifically, encoding the App Store URL directly is a density tax for no benefit. Always short-link first.

Print placement. App-download QR codes appear on outdoor signage, in airline magazines, on coffee-shop table cards, on car dashboards (yes, really). The print conditions are not uniform. Use level Q error correction at minimum, and assume the print won't be perfect. We covered the math on minimum QR code size for print — the short version: 2cm minimum for handheld, 4cm for sign-from-3-metres, scale linearly past that.

Fallback for the in-app-browser case. When someone scans your QR from inside the Facebook app or Instagram, the link opens in their in-app browser. The redirect happens, the user lands on apps.apple.com, but the "Open in App Store" button on that page is blocked by the WebView's link policy. The user is stuck on a web page that shows the app but can't install it. The fix: detect the in-app browser at the redirect layer and either render an interstitial page that says "open in Safari" with a one-tap link, or send JavaScript that calls window.open(url, '_blank') to escape the WebView. Branch.io and AppsFlyer both publish research showing in-app browsers cost 20–30% of intended app installs when the redirect doesn't handle them — which is most redirects.

20-30%
Of intended app installs lost to in-app browser handling when the QR's redirect doesn't detect Instagram/TikTok/Facebook WebViews and route around them. The fix is a 30-line server check, not a redesign.

Interactive — does your app QR setup pass the checks?

Run through the seven checks below. If your setup misses three or more, the QR is probably losing installs in production.

App-download QR pre-flight

0 / 7
Tick what your setup actually does today.

The list isn't comprehensive but it covers the cases that actually break in production. The setup that ticks all seven is roughly what a competent in-house team builds after losing a campaign's worth of installs to the in-app browser case.

Generate the QR, point it at a short link, and the platform handles the User-Agent routing automatically.

Try the QR generator

Tracking which scan turned into which install

The QR encodes a short link. The short link redirects to the App Store or Play Store. Where the user goes from there is — by default — invisible to you. The user lands in the App Store, taps Install, the app downloads, the app opens. None of those steps notify your server. There's no callback from the App Store to your redirect host saying "the user from scan #12345 just installed."

The way to close this loop is install attribution, which involves either an SDK in your app (AppsFlyer, Adjust, Branch, Singular) or — for simpler cases — the App Store and Play Store's own attribution APIs.

iOS uses Apple Search Ads Attribution (ASA) for paid installs and the broader AdAttributionKit (formerly SKAdNetwork) for cross-source attribution. The flow: your redirect appends a campaign parameter to the App Store URL (https://apps.apple.com/app/idXXX?pt=12345&ct=poster-q3&mt=8). The App Store passes that campaign data through to your app via the Install Source. Your app reads it on first launch. Apple's App Analytics campaign tracking documentation covers the parameter format. The pt (provider token), ct (campaign token), and mt (media type) parameters are the ones to care about.

Android uses Play Install Referrer. The redirect appends a referrer parameter to the Play Store URL (https://play.google.com/store/apps/details?id=com.yourapp&referrer=poster-q3). When the user installs, Play passes the referrer to your app via the Install Referrer API. Your app reads it on first launch.

The combination — User-Agent routing at the redirect, store-specific campaign parameters appended, in-app SDK reading the values on first launch — closes the loop. You can answer "the airport poster generated 312 scans, 178 installs, 41 7-day-active users." Without it, you have scans only. Useful but partial.

This is the same architecture as conversion tracking with QR codes and short links for non-app campaigns, with the store APIs replacing the web pixel.

Without install attribution, an app-download QR is a scan counter. With it, it's a campaign measurement system. The difference is roughly 50 lines of code spread across the redirect layer and the app's first-launch handler.

The three places app-download QRs break in production

After User-Agent edge cases and in-app browser handling, here's what's left of the production failure list. Each item is something that worked in QA and didn't in the wild.

1. The App Store URL is for the wrong country. App Store URLs include a country code (apps.apple.com/us/app/...). A US-locked URL works in the US but renders an "App not available in your region" page for users in other countries even when the app is globally available. The fix: omit the country code (apps.apple.com/app/... — Apple auto-routes by the device's region) or detect the request's Accept-Language header at the redirect layer and append the right country code. Same problem doesn't exist on Google Play; Play handles regionalisation on its end.

2. The Bundle ID changed when you re-published. Apps get re-published under new Bundle IDs surprisingly often — acquisitions, brand changes, agency hand-offs. The QR points at the old ID, which now returns a "this app was removed" page. The fix is the editable-destination property of dynamic QR codes — covered in dynamic QR types by default. Print enough physical QRs and you'll need this at least once over a 5-year horizon.

3. The fallback landing page isn't mobile-optimised. Desktop and weird-UA scans land on yourapp.com/download. If that page is your standard marketing site, it might be heavy, slow, or laid out for desktop. Mobile users who scanned from an unusual context (in-app browser fallback, for instance) hit a page that takes 8 seconds to load on cellular. They leave. The fix: a dedicated /download page that's lean — one screenshot, two store buttons, three lines of copy. Loads in under a second on 3G.

Smart App Banners — iOS Safari's freebie

Apple ships a feature called Smart App Banner that adds a "View in App Store" banner at the top of any Safari page that opts in. Add a single <meta> tag to your landing page and Safari handles the rest:

<meta name="apple-itunes-app" content="app-id=123456789, app-argument=yourapp://product/123">

When the user lands on your /download page in Safari on an iPhone, the banner appears. Tapping it either opens your app (if installed) or jumps to the App Store. Apple's Smart App Banners documentation covers the full spec.

Android has a similar feature via the Web App Manifest's related_applications field, but Chrome's banner is less aggressive than Safari's and most users don't see it. The iOS Smart Banner is the one worth wiring; the Android equivalent is nice-to-have.

The interaction with QR redirects: when your QR routes desktop and unusual UAs to the landing page, Smart App Banner provides a second chance for users on iOS Safari who somehow ended up there. The banner shows automatically. The user gets a one-tap install path even after the redirect classified them as "not mobile."

App-download QR codes are usually printed on surfaces where a single failure costs real money — airline magazines, transit ads, packaging at retail. Three print-side rules:

Size up. A QR on a poster scanned from 3 metres needs to be 10cm minimum. The decoder needs ~5x more linear modules than at handheld distance. Scale-too-small is the most common print failure mode in outdoor environments — see QR codes outdoors — billboards, bus stops, signage for the distance math.

Quiet zone matters more than size. The QR specification requires 4 modules of white space around every QR code (the "quiet zone"). Print designers regularly delete it because it makes the design feel busy. The result is a QR that scans on the proof and fails on the printed magazine. The 4-module margin isn't optional; it's how the decoder finds the code's boundary.

Logo placement breaks attribution. A centred logo overlay is fine up to 25% area at error-correction level Q. Past that, the code might scan in good light and fail at the in-flight magazine reading-light level. App campaigns can't afford the lossy version; ship a code at level H if you want logo room without risking the scan.

The decisions above are the same as any QR-code design — they just matter more when one scan equals one install attempt and the cost-per-install is measurable. We covered the broader how to design a custom QR code that actually scans playbook; here the only difference is the tighter conversion accountability.

What changes when you use Linked.Codes

The redirect layer is the part most app-download QRs get wrong. The short link absorbs the User-Agent detection, the country-code routing, the in-app browser handling, the install referrer parameter. The QR encodes one URL on your custom domain. The domain stays yours; the routing rules live in the dashboard.

The defaults in the QR codes platform docs handle level Q error correction, the 4-module quiet zone, and round modules at 105% size automatically. The short-link generator is the surface that creates the redirect — every Linked.Codes link supports device targeting, so the iOS/Android/desktop split is a checkbox, not a custom-code lift. When you change the App Store URL six months later because your Bundle ID rotated, you update the short link's destination. The print runs already in circulation keep working.

We don't claim install attribution — that requires SDK work inside your app and is the responsibility of your mobile team. What sits up to the App Store handoff is handled — User-Agent edge cases, the in-app browser detour, the iPad-in-desktop-mode workaround, the country-code routing, the scan analytics. The thirty percent of installs that other QR setups lose to those edge cases is the thirty percent you keep. To mock up the QR before wiring the redirect, the App Store QR code generator renders the artwork at print scale so you can verify quiet-zone and module size on the actual surface — packaging, poster, transit ad — before locking in the design.

Can one QR code work for both iPhone and Android?

Yes — that's the whole point of this setup. Encode a short link in the QR, set the short link to inspect User-Agent server-side and route per platform. iOS scans go to the App Store, Android scans go to Google Play, desktop scans go to your download landing page. One physical QR, three correct destinations.

Should I use a custom URL scheme like myapp:// in my QR?

No. Custom schemes fail silently when the app isn't installed — no error, no App Store fallback, no path forward for the user. Use a regular https:// URL that's a Universal Link (iOS) or App Link (Android). When the app is installed, it opens. When it isn't, the web layer handles the fallback to the App Store.

What about iPads — do they need special handling?

Yes. Since iPadOS 13, iPad's Safari defaults to "request desktop site" mode and the User-Agent claims to be macOS Safari rather than iOS. Naive detection treats the iPad as a desktop and sends it to the wrong fallback. Detect iPad via navigator.maxTouchPoints client-side or by matching Macintosh+Mobile in the User-Agent pattern server-side.

Do in-app browsers (Instagram, TikTok) break app QRs?

Often, yes. Scanning a QR from inside Instagram opens the link in Instagram's WebView, which blocks the App Store deep-link. The user lands on the App Store page but the "Open" button doesn't work. Detect in-app browsers at the redirect layer and either show an interstitial that says "open in Safari" or trigger a window.open() to escape the WebView.

Can I track which scan turned into an install?

Yes, with extra work. iOS uses campaign parameters appended to the App Store URL (pt, ct, mt) that pass through to your app via the Install Source. Android uses the Play Install Referrer API. Both require the app itself to read the parameter on first launch — that's an SDK or native-code change inside your app, not just a redirect change.

What if my app isn't on Google Play (Huawei, China, etc.)?

Detect the device at the redirect layer (Huawei sends HMSCore in the User-Agent; Chinese OEMs vary by manufacturer). If your app is on AppGallery, route Huawei devices there. If your app isn't on the alternate store, route to a landing page that explains why and offers a web app or APK download.

How big should an app-download QR code be on a poster?

Minimum 4cm for arm's-length reading, 10cm for 3-metre poster distance. Scale linearly past that — a billboard scanned from 10 metres needs roughly 30cm of QR. The decoder needs about 5 linear modules per cm of reading distance to lock on reliably.

Sourcesshow citations

Try it on your own domain

Branded short links and dynamic QR codes, on your subdomain or your own domain. One-time purchase, no per-click fees.