v150 · PWA · Manifest
Install State Simulator
Walk through a complete PWA origin migration from the user's perspective across four phases: pre-migration (app installed on old origin), developer deploys the migration handshake, Chrome verifies and presents the migration, and post-migration state. Each phase shows what Chrome does, what the user sees, and what data survives.
The PWA is installed at https://www.example.com/app/. The user has a shortcut on their desktop/homescreen. App data lives in the old origin's storage partition.
old origin
INSTALLED
→
INSTALLED
new origin
not installed
not installed
Current state: app lives at https://www.example.com/app/
-
UserApp shortcut on deviceUser installed the PWA from https://www.example.com/app/ at some point in the past. The app shortcut launches directly to this URL. All IndexedDB, localStorage, and service worker registrations are scoped to the old origin.
-
ChromeInstall record in ChromeChrome stores the manifest URL, app ID, and scope. The old manifest has no migration declarations yet. App ID at this point is typically the start URL.// Chrome's install record:
{ id: "https://www.example.com/app/", scope: "/app/" }
Data at this point
IndexedDB (old origin)present
localStorage (old origin)present
Service worker (old scope)active
Push subscriptionsactive
The developer deploys changes to both origins. The destination manifest declares the old installed app in
migrate_from. The old origin serves a web-app-origin-association file with allow_migration for the destination app ID.
-
ServerNew origin: destination manifest updatedSet a stable
"id"on the destination app and addmigrate_fromfor the old installed app ID. Usebehavior: "suggest"for a passive rollout or"force"when users cannot continue on the old app.// https://app.example.com/app/manifest.json
{ "id": "https://app.example.com/app/",
"migrate_from": [{ "id": "https://www.example.com/app/",
"behavior": "suggest" }] } -
ServerOld origin: web-app-origin-association file deployedThe old origin must serve a JSON file at
/.well-known/web-app-origin-associationauthorising the destination app ID. Without this explicitallow_migration, Chrome rejects the handoff.// GET https://www.example.com/.well-known/web-app-origin-association
{ "https://app.example.com/app/": {
"allow_migration": true
} } -
ServerOptional old manifest hintIf the old app can still receive manifest updates, add
migrate_toso Chrome can proactively discover the destination before the user visits the new origin.// https://www.example.com/app/manifest.json
{ "migrate_to": {
"id": "https://app.example.com/app/",
"install_url": "https://app.example.com/app/"
} }
On the user's next visit or app update check, Chrome fetches and verifies both sides. If all checks pass, Chrome presents the migration as an app update-style flow; a forced migration blocks the next launch until the user migrates or uninstalls.
-
ChromeFetch destination manifest and verify migrate_fromChrome fetches https://app.example.com/app/manifest.json and reads
migrate_from. It confirms the installed old app ID is listed and that the destination has a stableid. -
ChromeFetch old origin's web-app-origin-associationChrome GETs https://www.example.com/.well-known/web-app-origin-association and verifies the entry keyed by the destination app ID has
allow_migration: true. This confirms the old origin consents to the migration. -
ChromeReview migration and update install recordChrome shows an update-style migration prompt, or a blocking forced migration if the manifest requested
behavior: "force". After the user accepts, the shortcut points at the new origin and the app ID is updated to the destination manifest ID.// Install record after migration:
{ id: "https://app.example.com/app/", scope: "/app/",
origin: "https://app.example.com" }
Migration complete. The app shortcut now opens the new origin. The user sees no interruption. What happens to storage data depends on whether both origins share data before migration.
old origin
redirected
→
redirected
new origin
INSTALLED
INSTALLED
App now lives at https://app.example.com/app/
Data survival
IndexedDB (old origin)old partition — migrate manually
localStorage (old origin)old partition — migrate manually
Service workerold SW still registered — may need update
Push subscriptionsold origin — re-subscribe on new
App shortcutpreserved — now opens new origin
App ID in Chromeupdated to destination manifest id
Storage note: origin-scoped APIs (IndexedDB, localStorage, Cache API) use origin as the partition key. Data on the old origin is not automatically copied to the new one. Your app's first load on the new origin should check for and migrate any critical user data from the old origin (using cross-origin fetch or redirects) before the old origin is retired.
see also
- Verification Checker — checklist of all Chrome 150 verification steps
- Manifest Builder — generate both required files
- Manifest Configuration — field reference
- ChromeStatus entry
- Chrome Developers — Seamless PWA origin migration
implementation reference
Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗