PWA Manifest Requirements: The Fields Browsers Actually Check (2026)
A browser decides whether your site can be installed by reading one small JSON file. Most "why is there no install button" tickets come down to a missing icon size or a display value the browser ignores — not to anything complicated. This is the current list of what Chromium actually checks, what is optional, and what is a myth.
The required fields
For Chromium browsers — Chrome, Edge, Samsung Internet, Opera — a web app manifest must contain:
|
Field |
Requirement |
|---|---|
|
|
At least one must be present |
|
|
Must include a 192px and a 512px icon |
|
|
Required |
|
|
Required |
|
|
Must be |
A minimal manifest that satisfies all five:
{
"name": "Your App Name",
"short_name": "YourApp",
"start_url": "/",
"display": "standalone",
"icons": [
{ "src": "/icons/192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/512.png", "sizes": "512x512", "type": "image/png" }
],
"prefer_related_applications": false
}Two more conditions live outside the file itself:
- The page must be served over HTTPS, or from
localhost/127.0.0.1during development. There is no exception for staging domains on plain HTTP. - The manifest must be linked from the page:
<link rel="manifest" href="/manifest.webmanifest">, and it must be reachable — a manifest behind auth or blocked by CORS fails silently.
The service worker myth
The most persistent piece of outdated advice is that installability requires a service worker with a fetch handler. That was true of older Chromium versions; it is not a requirement today. A site can be installable with no service worker at all.
That does not make service workers pointless — they are still how you get offline behaviour, caching, and background sync, which is a different goal from being installable. If your reason for adding one was purely to unlock the install prompt, that reason is gone; if it was to keep the app usable on a bad connection, it still stands. We covered the real boundaries of that in PWA offline support.
Fields that are optional but change what users see
These do not gate installability, and they do decide how the install looks:
screenshots+description— Chromium shows a richer install dialog, closer to an app store listing, when these are present. Without them users get a plain one-line prompt.id— the stable identity of the app. If you omit it, the identity is derived fromstart_url; changingstart_urllater then registers as a different app rather than an update.scope— which URLs stay inside the installed window. Links outside scope open in a browser tab.theme_color/background_color— the toolbar tint and the splash background. A missingbackground_colorgives you a white flash on launch.icons[].purpose: "maskable"— lets Android crop your icon into the platform shape without white borders. Supply a maskable variant in addition to the standard icons, not instead of them.shortcuts— long-press menu entries on the home screen icon.orientationandcategories— situational; harmless to include.
Browser support is not uniform
Meeting the Chromium checklist does not mean every browser installs your app the same way.
|
Platform / browser |
Behaviour |
|---|---|
|
Chrome / Edge desktop |
Full manifest-driven install |
|
Firefox desktop |
No manifest-based install |
|
Android (Chrome, Edge, Firefox, Opera, Samsung Internet) |
Supported |
|
macOS Safari (Sonoma and later) |
"Add to Dock" — works without a manifest |
|
iOS 16.3 and earlier |
Safari only |
|
iOS 16.4+ |
Safari, Chrome, Edge, Firefox, Orion — via the Share menu |
The practical consequence: on iOS the install is a manual user gesture through the Share sheet, so your onboarding has to tell people to do it. There is no programmatic prompt to fire. See how the install prompt behaves across Android and iOS for the flow differences.
How to verify instead of guessing
- DevTools → Application → Manifest. Chromium lists the parsed manifest and prints an explicit reason under installability when something is missing.
- Lighthouse → Installability audit. Run it against the production URL, not localhost — HTTPS and header problems only appear on the real host.
- Fetch the manifest as an anonymous client:
curl -I https://yoursite/manifest.webmanifest. Confirm200and a JSON content type. A manifest that returns your SPA's HTML shell is the single most common silent failure. - Test on a real Android device. Emulator install behaviour and desktop behaviour both differ from the phone your users hold.
Common reasons a valid-looking manifest still fails
- Only a 512px icon is declared, or the icons are declared with the wrong
sizesstring, so the 192px requirement is unmet. displayis set to"browser", which explicitly opts out of an app window.start_urlreturns a redirect to a login page, or sits outsidescope.- The manifest is served with
Content-Type: text/htmlby a catch-all route. - The site is on HTTP, or on HTTPS with a certificate the device does not trust.
prefer_related_applicationsistrue, left over from a template that pointed at a native app listing.
If you are still deciding whether an installable web app is the right distribution route at all, the trade-offs against packaged Android distribution are in PWA vs APK, and the difference from a Trusted Web Activity is in PWA vs TWA.
Frequently asked questions
Does a PWA need a service worker to be installable?
No. Current Chromium installability rules do not require a service worker. You still need one for offline support, caching, and background features — those are separate goals from installability.
Which icon sizes are actually required?
A 192px and a 512px icon. Additional sizes and a maskable variant are recommended for icon quality on Android, but they are not part of the requirement.
Can a PWA be installed over HTTP?
Only from localhost or 127.0.0.1 for local development. Any other host must serve over HTTPS with a valid certificate.
Why is there no install prompt on iPhone?
iOS does not expose a programmatic install prompt. Users add the app from the Share menu — Safari on all supported versions, and Chrome, Edge, Firefox or Orion on iOS 16.4 and later. Your UI has to guide that gesture.
Does changing start_url break my installed app?
It can. Without an explicit id, app identity is derived from start_url, so changing it makes browsers treat the result as a new app rather than an update to the installed one. Set id early.


