A deep dive into Chrome 150's new PWA origin migration feature — the two-way handshake, suggest vs force modes, security considerations, and a complete step-by-step migration checklist for developers.
Since Progressive Web Apps were introduced, one of their most painful limitations has been
the tight coupling between a PWA's identity and its web origin. If you
deployed a PWA at www.example.com/social and later needed to move it to
social.example.com, there was no supported path. Users who had installed your
app were stuck — the only option was to manually uninstall, find the new install button, and
reinstall.
Changing the start_url without a stable id field created a worse
problem: the browser treated the new start_url as a completely different app, creating
duplicate installations — the infamous "split app" bug. Users ended up with
two icons for the same app, one pointing to the old path and one to the new.
Chrome 150 introduces PWA Origin Migration — a platform feature that solves this problem once and for all. Developers can now transition installed PWAs to a new same-site origin with a single-click update dialog for users. Announced on June 3, 2026, this is one of the most significant PWA platform improvements since the ability to install PWAs on desktop.
The feature unlocks several practical scenarios that were previously impossible or required painful workarounds:
Move your app between subdomains or paths without losing your installed user base:
app.example.com → saas.example.com (rebranding a product)example.com/old-app → new-app.example.com (spinning out a product)example.com/user → user.example.com (dedicated subdomain for a feature)
If your PWA's start_url changed at any point without a stable id,
existing users ended up with duplicate installations. Origin migration provides a clean way
to consolidate these split installations into a single, properly identified app.
Companies rebrand, domains change, and URL structures evolve. With PWA origin migration, you can move your installed PWA to follow your new domain strategy. This is especially valuable during corporate mergers, acquisitions, or strategic pivots where domain changes are inevitable.
If you started with a multi-tenant PWA on a shared subdomain and later need to split it into dedicated domain-based apps per tenant, origin migration makes this possible without losing any installed user base.
PWA origin migration uses a two-way handshake protocol between the old and new origins. Both sides must explicitly participate — no silent takeovers are possible. Let's walk through each step.
The destination (new) app's web app manifest must include a
migrate_from field listing the origin(s) it's migrating from.
// Manifest at https://new-app.example.com/manifest.json
{
"name": "New App Name",
"id": "/app/",
"start_url": "/app/index.html",
"migrate_from": [
"https://old-app.example.com/"
]
}
Important: The id field in the destination manifest is
required. Without it, the browser cannot reliably associate the new
installation with the migrated app identity, and you risk creating the very split-app
scenario you're trying to fix.
The old origin must explicitly authorize the migration by deploying a well-known configuration file. This prevents hostile takeovers — a new site cannot unilaterally hijack an existing app's installation base.
// File at https://old-app.example.com/.well-known/web-app-origin-association
{
"https://new-app.example.com/app/": {
"allow_migration": true
}
}
This file is served from the old origin's .well-known
directory, mirroring the pattern used by other web platform features like
web-app-origin-association for URL handling. The browser fetches this file
during the migration flow to verify authorization.
To trigger migration proactively — without waiting for users to visit the new site — update
the old app's manifest with a migrate_to field pointing to
the new origin.
// Updated manifest at https://old-app.example.com/manifest.json
{
"name": "Old App",
"start_url": "/",
"migrate_to": {
"id": "https://new-app.example.com/app/",
"install_url": "https://new-app.example.com/app/install"
}
}
When the browser sees the migrate_to field in the old manifest, it presents the
migration option to users proactively — they don't need to navigate to the new site first.
If you've set up redirects from old URLs to the new origin, include an
install_url inside the migrate_from field. This tells the browser
where to find the old manifest without following redirects.
// Extended migrate_from with install_url
{
"migrate_from": [
{
"id": "https://old-app.example.com/",
"install_url": "https://old-app.example.com/installwebapp?from=migrate"
}
]
}
Pro tip: Don't skip install_url if your old URLs redirect to the new ones. Without it, redirect chains can prevent the browser from reading the old manifest, and the migration flow may not trigger correctly.
Chrome 150 supports two migration behavior modes that control how the migration is presented to users:
Behavior: A passive notification appears in the app menu. Users can choose to update, uninstall, or ignore the migration entirely.
When to use: Initial rollouts, A/B testing, gradual migrations where you want to minimize user disruption. Users who are ready to migrate can do so; those who prefer the old experience can stay.
Behavior: A blocking dialog appears on the next app launch. Users must either update or uninstall before they can continue using the app.
{
"migrate_from": [
{
"id": "https://old-app.example.com/",
"behavior": "force"
}
]
}
When to use: When the old origin is being deprecated, the old domain is expiring, or for enterprise migrations where all users must move to the new origin by a deadline.
Even in force mode, name and icon changes are deferred.
After migration completes, the app's display name and icon are updated through the standard
app update process — not during the migration dialog. This prevents confusion where a user
sees an entirely different app name in the migration confirmation.
Recommendation: Deploy with suggest first, monitor adoption
rates via your analytics, then switch to force once you're confident the
new origin is stable and users are accustomed to the transition.
Here's a complete reference implementation covering all five files you need to deploy for a
full PWA origin migration from old.example.com to new.example.com.
Every file is required for a successful migration flow.
The destination manifest declares the predecessor origin and specifies the migration behavior.
// https://new.example.com/manifest.json
{
"name": "App Rebranded",
"short_name": "AppRebrand",
"id": "/app/",
"start_url": "/app/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0f766e",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"migrate_from": [
{
"id": "https://old.example.com/",
"behavior": "suggest"
}
]
}
Deployed on the old origin to authorize the new one. Without this file, the browser refuses the migration.
// https://old.example.com/.well-known/web-app-origin-association
{
"https://new.example.com/app/": {
"allow_migration": true
}
}
Updated on the old origin to proactively push the migration to users who haven't visited the new site yet.
// https://old.example.com/manifest.json
{
"name": "Original App",
"id": "/",
"start_url": "/",
"display": "standalone",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
],
"migrate_to": {
"id": "https://new.example.com/app/",
"install_url": "https://new.example.com/manifest.json"
}
}
The new origin's service worker handles cache migration and provides fallback behaviour during the transition period.
// https://new.example.com/sw.js
const CACHE_NAME = 'app-v2';
self.addEventListener('install', (event) => {
// Pre-cache core assets on the new origin
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll([
'/app/',
'/app/index.html',
'/app/styles.css',
'/app/app.js'
]);
})
);
});
self.addEventListener('activate', (event) => {
// Claim clients immediately on new origin
event.waitUntil(clients.claim());
// Clean old caches
event.waitUntil(
caches.keys().then((keys) => {
return Promise.all(
keys.filter((k) => k !== CACHE_NAME)
.map((k) => caches.delete(k))
);
})
);
});
self.addEventListener('fetch', (event) => {
// Serve from cache with network fallback
event.respondWith(
caches.match(event.request).then((cached) => {
return cached || fetch(event.request);
})
);
});
Server-level redirects ensure old URLs gracefully forward to the new origin while the migration is in progress. Shown here for NGINX.
# /etc/nginx/sites-available/old.example.com
server {
listen 443 ssl;
server_name old.example.com;
# Redirect all traffic to the new origin
# The install_url parameter preserves the migration flow
location / {
return 301 https://new.example.com/app/$request_uri;
}
# Well-known must remain accessible on old origin
location /.well-known/ {
alias /var/www/old-app/.well-known/;
try_files $uri =404;
}
}
Production tip: Deploy the well-known file and old manifest (Files 2 and 3) on the OLD origin at least 48 hours before publishing the new manifest (File 1). This gives the browser cache time to pick up the authorization and proactive signal, ensuring a smooth migration window.
While PWA origin migration handles the installation record transfer, application data — IndexedDB, Cache Storage, and service worker state — does NOT automatically migrate. You need a deliberate strategy for data continuity.
If both origins share the same backend (e.g., same API server), you can simply re-fetch user data from the server. The service worker on the new origin should:
// Service worker on new origin: data migration strategy
const DATA_MIGRATION_KEY = 'data-migration-v1';
self.addEventListener('activate', (event) => {
// Check if this is a post-migration activation
event.waitUntil((async () => {
const migrated = await caches.match(DATA_MIGRATION_KEY);
if (!migrated) {
// Fetch user data from the shared backend
const response = await fetch('/api/user/data', {
credentials: 'include'
});
if (response.ok) {
const cache = await caches.open('user-data');
await cache.put('/api/user/data', response);
await cache.put(DATA_MIGRATION_KEY,
new Response('migrated', { status: 200 }));
}
}
})());
});
Send a push notification or in-app prompt after migration instructing the client to re-cache critical data. This is the safest approach and mirrors how many major PWAs handle major version updates today.
Data migration tip: Test the full migration flow (install → migrate → launch on new origin) with Chrome Canary before rolling out to production. Pay special attention to IndexedDB databases — their origin-scoped storage does NOT follow the app to the new origin. Plan for a server-side data sync or a user-facing "restore your data" flow on first launch.
The PWA origin migration feature was designed with security as a first-class concern:
Only same-site migrations (same eTLD+1) are supported. You can migrate from
app.example.com to saas.example.com, but not from
example.com to different-domain.com. This restriction prevents
cross-organization app hijacking.
The .well-known/web-app-origin-association file on the old origin serves as
the authorization mechanism. Without this file explicitly permitting the migration, the
browser will not proceed. This prevents any silent takeover scenario.
For organizations using the WebAppInstallForceList policy to manage PWA installations, there's an important caveat: enterprise-managed apps are blocked from automatic migration. Because enterprise policies are URL/origin-based, a migration could bypass admin controls by moving a managed app to an unmanaged origin. Users will see a banner explaining that policy settings block the migration.
Enterprise administrators should plan migrations through group policy updates, not through the origin migration feature directly.
Follow this checklist to migrate your PWA seamlessly:
id field to the destination manifest (required!)migrate_from on the new origin.well-known/web-app-origin-association on the old origin authorizing the migrationsuggest (gradual) or force (mandatory)migrate_to on the old manifest for proactive signalinginstall_url if old URLs redirect to new onesPro tip: For large user bases, deploy with suggest first, monitor adoption for 1-2 weeks, then switch to force. Test every step with Chrome Canary before touching production — it's always ahead of the stable release.
| Aspect | Before Chrome 150 | After Chrome 150 |
|---|---|---|
| Domain Change | Manual uninstall + reinstall | One-click migration dialog |
| User Interruption | High — users had to find install button | Minimal — familiar update UX |
| Data Loss Risk | Users might not reinstall → churn | Automatic transition preserves installs |
| Security | No formal mechanism | Two-way handshake with .well-known verification |
| Enterprise Control | Not applicable | Policy-controlled via WebAppInstallForceList |
| Migration Scope | Same origin only | Same-site (eTLD+1) supported |
| Developer Effort | Manual redirects + user communication | 4-step declarative configuration |
PWA origin migration was announced in Chrome 150 beta on June 3, 2026. The stable release is expected approximately 6 weeks later, around July 2026, following Chrome's standard release cycle.
Current support:
As with most new PWA platform features, Safari and Firefox have not yet signaled support.
However, because the feature relies on standard web manifest fields and well-known files,
it doesn't break anything in non-supporting browsers — the migrate_from and
migrate_to fields are simply ignored.
Speculative future: Cross-site migration support (for genuinely related but different domains) would be the next logical evolution, though it introduces significant security and trust challenges. Broader browser adoption will likely follow as the feature matures.
Why this matters for web developers: This is a brand-new feature with essentially zero competition in search results as of June 2026. Being an early publisher on this topic positions your content for long-term organic growth as developers search for migration guides when Chrome 150 reaches stable.
.well-known/web-app-origin-association file to confirm the migration. Only same-site (same eTLD+1) migrations are supported, preventing cross-organization hijacking. Enterprise-managed apps installed via the WebAppInstallForceList policy are protected from automatic migration.app.example.com to saas.example.com, but not from example.com to different-domain.com. Cross-site migration is not supported for security reasons.id field in the destination web app manifest is required for PWA origin migration. Without it, the browser cannot properly associate the new installation with the migrated app identity, potentially creating a split-app scenario where users end up with duplicate installations.PWA origin migration is just one piece of the Progressive Web App puzzle. Building a successful PWA requires thoughtful architecture, careful consideration of offline strategies, service worker management, and performance optimization across devices. See my web development services for a full range of what I can build for you.
I'm a full-stack web developer with deep experience building PWAs for businesses in Minsk and worldwide. If you're planning a PWA project, considering origin migration, or need an expert review of your current PWA architecture, reach out to me. I provide free initial consultations.
Whether you're migrating to staged publishing, setting up CI/CD for npm packages, or auditing your supply chain security — I can help. Free consultation.