Manifest member in Manifest
scope_extensions manifest member
Published
scope_extensions is an array of origins that an installed web app wants treated as part of
its scope, so that support.example.com or example.co.uk opens inside the app window
without the out-of-scope bar. The manifest scope member can only cover one origin; this
member is the two-sided handshake that lets an app span several, with each extra origin
confirming the association in a /.well-known/web-app-origin-association file.
Chrome 138, Edge 138, Android WebView 138, and Samsung Internet 30.0 ship the member (BCD
html.manifest.scope_extensions); Chrome ran it as an origin trial in 121 to 126 and behind
about://flags/#enable-desktop-pwas-scope-extensions from 115. Firefox 157 and Safari 27 do not
read it, and drop the member silently.
Member
Section titled “Member”- Type: array of objects. The Manifest Incubations explainer and MDN write each entry as
{ "type": "origin", "origin": "https://support.example.com" }; Chrome’s developer documentation, written for the origin trial, omitstypeand writes{ "origin": "..." }.originis an HTTPS origin, optionally with a*.wildcard label for all subdomains, as inhttps://*.example.com. - Default: an empty array; the app’s scope is the single-origin
scopemember. - Example value:
[{ "type": "origin", "origin": "https://*.example.com" }].
An entry takes effect only after the browser fetches
https://<origin>/.well-known/web-app-origin-association from the listed origin and finds the
app’s manifest id in it. The two sources disagree on that file’s shape as well: the explainer
and MDN key the file by app id, { "https://example.com/app": { "scope": "/" } }, where the
optional scope narrows the extension to a path on the extended origin; Chrome’s origin-trial
documentation shows { "web_apps": [{ "web_app_identity": "https://example.com" }] }. Serve
the shape your target Chromium version validates, and treat the association as a statement of
trust: an origin that lists an app lets that app present the origin’s pages as its own.
Permissions do not travel with the extension. A page on support.example.com opened inside
the example.com app window keeps support.example.com’s permission state, storage, and
cookies; only the window chrome changes.
Examples
Section titled “Examples”The manifest and the association file are one declaration split across two servers; the third example is what the page can learn once both are in place.
Extending an app to its support subdomain and a country domain
Section titled “Extending an app to its support subdomain and a country domain”The main app at https://example.com/app lists two extra origins. The wildcard form covers
every subdomain of example.com, so support. and help. need no separate entries; the
country domain is a different registrable domain and is listed on its own.
{ "id": "/app", "name": "Example", "start_url": "/app/index.html", "scope": "/app", "display": "standalone", "scope_extensions": [ { "type": "origin", "origin": "https://*.example.com" }, { "type": "origin", "origin": "https://example.co.uk" } ]}Each listed origin must answer for itself; a missing or malformed association file on
example.co.uk leaves that origin out of scope while the subdomains still work.
Serving the association file on the extended origin
Section titled “Serving the association file on the extended origin”https://example.co.uk/.well-known/web-app-origin-association is served as JSON with the
application/json type and no authentication. The explainer’s shape keys the file by the
app’s full manifest id and scopes the extension to a path.
{ "https://example.com/app": { "scope": "/" }}For a Chromium build that validates the origin-trial shape, the same statement reads
{ "web_apps": [{ "web_app_identity": "https://example.com/app" }] }. Publishing both forms in
one file is not possible, so check the parsed result in chrome://web-app-internals after
installing.
Telling the page whether it is inside the extended app window
Section titled “Telling the page whether it is inside the extended app window”There is no API that reports whether a navigation was accepted as in scope. What the page can
observe is the display mode: a document on support.example.com that finds itself in
standalone was opened inside an app window, which on Chrome 138 and later means the
extension validated. The fallback branch is a browser tab, where related-origin links behave
as ordinary cross-origin navigations.
const inAppWindow = ['standalone', 'minimal-ui', 'window-controls-overlay'] .some((mode) => matchMedia(`(display-mode: ${mode})`).matches);
if (inAppWindow) { document.body.classList.add('in-app'); // hide the marketing header, keep the app nav} else { // Browser tab, or an engine without scope_extensions: links to example.com // open as normal navigations and may show the out-of-scope bar in an older app window. document.body.classList.add('in-tab');}Keep the navigation itself identical in both branches: location.assign() to the related
origin is correct whether or not the extension is honoured, and the browser decides what chrome
to show.
See also
Section titled “See also”- scope manifest member
- id manifest member
- handle_links manifest member
- Scope Extensions for Web App Manifest (explainer) (github.com)
- Web app scope extensions (developer.chrome.com)
- scope_extensions (developer.mozilla.org)
Specifications
| Specification | Status |
|---|---|
| manifest: scope_extensions | Explainer |
| Web Application Manifest: scope member | W3C |
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Desktop) | Yes | 138 | high | source | — |
| Chrome (Android) | Yes | 138 | high | source | 1 |
| Edge (Desktop) | Yes | 138 | high | source | 2 |
| Firefox (Desktop) | No | — | high | source | 3 |
| Firefox (Android) | No | — | high | source | 45 |
| Safari (macOS) | No | — | high | source | 6 |
| Safari (iOS) | No | — | high | source | 78 |
| Samsung Internet | Yes | 30.0 | high | source | 9 |
| WebView (Android) | Yes | 138 | high | source | 10 |
- Derived by browser-compat-data mirroring from Chrome.
- Derived by browser-compat-data mirroring from Chrome.
- No Firefox support is recorded in browser-compat-data.
- No Firefox for Android support is recorded in browser-compat-data.
- Derived by browser-compat-data mirroring from Firefox.
- No Safari support is recorded in browser-compat-data.
- No Safari on iOS support is recorded in browser-compat-data.
- Derived by browser-compat-data mirroring from Safari.
- Derived by browser-compat-data mirroring from Chrome Android.
- Derived by browser-compat-data mirroring from Chrome Android.