Konubinix' opinionated web of thoughts

How to Make a PWA Install as a WebAPK

Braindump

On Android the same PWA can install two very different ways. One is a real app: its own icon in the launcher, its own entry in the system’s app settings, no browser chrome, and it captures links into its scope. The other is a bookmark wearing a small Chrome badge that reopens in a tab. The first is a WebAPK, the second a shortcut. Which one you get is not a user choice — it is a verdict Chrome passes on your manifest at install time. This note is the recipe for earning the WebAPK verdict, distilled from getting score counter tally to mint one after it sat stuck as a shortcut for months.

The mint is a remote build

Chrome does not build the APK itself, and it does not always build one at all. When it judges the page installable it drives the mint shown below; when it does not, it quietly falls back to a badged bookmark. That fork — real app or shortcut — is the whole game, and everything after this section is about landing on the installable branch.

What earns the installable verdict

Chrome enumerates the exact reasons it refuses an install in its own source (components/webapps/browser/installable/installable_logging.cc). Stripped to what a hand-written app must provide:

  • served over HTTPS (localhost excepted for testing);
  • a linked manifest with a name or short_name;
  • a valid start_url, with no username, password, or port in any manifest URL;
  • a display of standalone, fullscreen, or minimal-ui — never browser;
  • no prefer_related_applications: true;
  • icons covering at least 192 and 512 px in PNG, SVG, or WebP, each with a sizes attribute and, if purpose is set, a value including any or maskable.

A service worker is no longer part of this gate (Chrome dropped that requirement in version 108), but registering one is still worth it: it is what lets the installed app open offline instead of to a blank page.

Every icon must be a real file Chrome can fetch

This is the trap that is easy to miss, because the manifest looks valid and the icons render fine in the browser. The icons a manifest lists are downloaded — by Chrome for the installability check, then by the minting server to bake into the APK. An icon Chrome cannot download or decode fails the check with « downloaded icon was empty or corrupted », and the whole install drops to a shortcut. An inline data: URI icon has no URL to fetch, so it sinks the install even when perfectly good PNG files sit right beside it in the array. The fix is blunt: every entry in icons points at a real file the server serves. That single change is what turned score counter tally from a shortcut into a WebAPK.

The tell is unambiguous: an app that only ever became a shortcut never appears at all in chrome://webapks, because no WebAPK was ever minted for it.

The recipe

The reference implementation is the shared manifest and service worker in PWA score apps — shared blocks; every app below tangles from it or mirrors it. Concretely, the manifest each installing app ships:

  • name / short_name, start_url ., and scope . so the app owns its path;
  • display standalone (or fullscreen);
  • three icons, all served files: icon-192.png and icon-512.png (rendered from an SVG, purpose any maskable) plus the icon.svg itself at sizes any;
  • a registered service worker, over HTTPS.

Confirm it worked

Automated tests can only assert the shipped preconditions — that the manifest is served, declares both sizes, and that every icon it lists is a fetchable file with no data: URI. They cannot drive a real mint, which happens on a Google server. So the last check is on the device: install the app, then open chrome://webapks. If the app is listed there, it minted a WebAPK; if it is absent, you got a shortcut and one of the gates above is still failing.

Notes linking here