1. Host permissions
A host permission is a match pattern listed in
host_permissions, or granted at runtime per § 1.2 Granting host permissions. Together
the extension’s granted host permissions form its granted host
permissions.
An extension has access to a URL url if all of the following are true:
-
url is not restricted per § 1.1 Restricted URLs.
-
url is matched by a match pattern in the extension’s granted host permissions, or by a temporary host permission granted through § 1.4 User gestures and activeTab.
Access to a URL is the single gate used throughout this specification
and by the browser APIs for at least:
-
Injecting a content script into a document (see Inject a content script).
-
Observing requests to or from a URL, for example with
webRequest. -
Reading or modifying cookies for a URL, for example with
cookies. -
Exposing a tab’s
url,title, andfavIconUrlto the extension, unless the extension also has thetabspermission. -
Applying a declarativeNetRequest rule that modifies or redirects a matched request (
modifyHeaders,redirect); a rule that only blocks or upgrades a request’s scheme (block,upgradeScheme) does not require host access for the matched URL. Modifying or redirecting a request can be used to relax cross-origin protections (for example via CORS or CSP response headers) or otherwise affect content the extension does not otherwise have access to (w3c/webextensions#782, closed 2025-03-27).
1.1. Restricted URLs
A host permission grants access to a URL only when the URL’s
scheme is one of the schemes permitted in a match pattern;
<all_urls> is no exception. This governs every access path that is
mediated by host permissions (content scripts, and API access such as
webRequest, cookies, and tabs). It does not by itself describe a
browser-specific debugging API such as Chrome’s debugger permission,
which is out of scope for this section; where such an API exists, an
implementation should state its own access boundary rather than relying on
this one, since the two need not coincide. In Chrome specifically, that
debugging API bypasses the requirement that a URL fall within the
extension’s granted host permissions, while remaining subject to the
scheme and specific-host restrictions in this section. External remote
debugging of a browser instance is outside this specification’s scope
regardless.
Implementations additionally restrict specific hosts and schemes beyond what match pattern parsing alone would allow, independently of whether a granted match pattern would otherwise match:
-
Pages served from the extension’s own store or gallery, if the implementation has one.
-
The implementation’s own privileged/internal pages (browser UI pages), except where an implementation-specific flag or a component or policy-installed extension overrides this.
-
Another extension’s own pages, except where an implementation-specific flag or a component or policy-installed extension overrides this.
-
Domains an implementation-specific allowlist marks as always off-limits, for example to protect account-management or extension-store login flows.
Should this specification normatively list restricted hosts and schemes, require the restricted set to be discoverable at runtime, or leave it implementation-defined as today?
No implementation currently exposes the restricted set to an extension at runtime.
1.2. Granting host permissions
A match pattern listed in host_permissions is a required host permission.
An optional host permission is a host permission the extension may hold without it
being granted at install: a pattern listed in optional_host_permissions,
or one listed in host_permissions that the user agent has withheld until
requested. An optional host permission is not part of the extension’s granted host permissions until granted.
Chrome and Firefox both grant required host permissions automatically at install time, then let the user subsequently withhold a granted host permission from a specific site through browser UI, at which point the extension’s effective access to that site reverts to a runtime-request flow even though the permission is still listed as required in the manifest. WebKit’s engine performs no automatic grant at all, for either required or optional host permissions; the embedding application is responsible for presenting required permissions to the user (for example at install or update) and recording the result, and nothing in the engine forces this to happen or guarantees a required host permission is granted by the time the extension runs.
This specification does not require required host permissions to be
granted automatically at install, nor does it require user consent before
every use. The standardized optional_host_permissions manifest key lets
an extension author declare which host permissions are not required for
core functionality and must be requested at runtime via
request(); whether permissions listed in host_permissions
are granted automatically at install, deferred to a runtime consent step,
or made revocable or withholdable by the user is left to the
implementation (w3c/webextensions#119, closed 2022-10-18).
To request a set of host permissions, an extension calls
request() with those match patterns in origins.
This may only be called from a privileged extension context and only
while handling a user activation. The implementation must either add
each requested pattern to the extension’s granted host permissions,
or deny the entire request; a partial grant of a single request()
call is not observable by the extension.
{ "optional_host_permissions" : [ "https://*.example.com/*" ] }
let granted= await browser. permissions. request({ origins: [ "https://*.example.com/*" ] });
1.3. Cross-origin fetch
A request made from a privileged extension context whose target URL is
matched by the extension’s granted host permissions is exempt from Fetch § 3.3 CORS protocol: the request
proceeds, and the response is made available to the requesting script,
without regard to Access-Control-Allow-Origin or any other CORS
response header.
A request made from a content script context is not exempt from
CORS on the basis of the extension’s host permissions. Such a request is
subject to exactly the cross-origin rules that would apply to a request
made by the page the content script is running within: same-origin
requests succeed as normal, and cross-origin requests succeed only if the
target server’s response grants access under Fetch § 3.3 CORS protocol.
A granted host permission does not change this outcome, even
<all_urls>.
-
Chrome removed cross-origin content script fetches as a platform change (Chrome 73 CORB, Chrome 85 CORS enforcement, allowlist fully removed in Chrome 87), independent of manifest version; MV2 and MV3 extensions in current Chrome both get page-origin-only fetch from content scripts.
-
Firefox gates this explicitly on manifest version: an MV2 content script’s
fetch/XMLHttpRequest/WebSocketrun with a principal that includes the extension’s own principal (bypassing CORS via host permissions, per Firefox’s own source comments), while an MV3 content script gets no such override and inherits the page’s ownfetch/XHR. -
Safari’s engine wires its CORS bypass configuration only into extension-page web views, and that wiring does not branch on manifest version at all, unlike a neighboring property in the same code path that does. Content scripts run inside the tab’s own web view, in an isolated script world that never receives the bypass configuration. Nothing in the engine, for either manifest version, ever gives a content script a cross-origin fetch bypass.
Safari’s content-script fetch behavior has therefore never differed by manifest version: content scripts have always been subject to the same cross-origin rules as the page, matching Chrome’s and Firefox’s current (post-MV3-convergence) behavior rather than Firefox’s legacy MV2 exemption.
"host_permissions": ["https://api.example.com/*"] can
call fetch("https://api.example.com/data") from its background service
worker regardless of api.example.com’s CORS headers. The same call made
from a content script injected into https://news.example/ succeeds only
if https://api.example.com/data’s response includes an
Access-Control-Allow-Origin header permitting https://news.example.
1.4. User gestures and activeTab
A user activation is transient activation of the document
handling the interaction, extended to browser-chrome surfaces that have no
document at all: clicking the extension’s toolbar action, invoking one of
its keyboard commands, choosing one of its context menu
items, or (implementation-defined) selecting it from the address bar. A
user activation triggered from one of those surfaces is scoped to the
extension it was directed at, the same way transient activation is
scoped to a browsing context.
If an extension declares the activeTab permission, then each
time a user activation for that extension occurs while a tab is active,
the extension is granted a temporary host permission for that
tab: for the remainder of the tab’s current navigation, the extension has
access to a URL matching the tab’s current URL as if that URL were
covered by a granted host permission, and the extension is granted
the tabs permission for that tab (exposing its url, title,
and favIconUrl).
A temporary host permission does not persist across navigation of the
tab’s top-level document to a different origin; a fresh user activation
is required after such a navigation. It does not grant access to any
other tab, and it does not add anything to the extension’s granted host permissions as observed by getAll().
activeTab creates revoked? Chrome, Firefox, and Safari disagree:
-
Chrome revokes it when the tab’s main frame completes a committed, cross-origin navigation (same-origin navigations, and any subframe navigation, leave it intact).
-
Safari revokes it when the tab’s main frame commits a URL no longer matched by the granted pattern.
-
Firefox does not revoke it on navigation at all: source comments state this is a deliberate difference from Chrome, and the grant instead tracks the surviving lifetime of the tab’s inner window (including back/forward-cache revival), being cleared only when the extension’s action is invoked again for a different tab or the tab/window is destroyed.
Tracked at w3c/webextensions#657.
A user clicks the extension’s toolbar icon while viewing{ "permissions" : [ "activeTab" ] }
https://example.com/. The extension is granted access to
https://example.com/ for that tab until the tab navigates to a
different origin; it still has no access to any other tab, and no access
to https://example.com/ in a new tab the user opens afterward.