Chrome Extension Site Access

Host Permissions vs activeTab in Chrome Extensions

Compare ongoing host access with temporary access after a click, using two Manifest V3 examples that run the same page-marking code.

Host permissions vs activeTab at a glance

Questionhost_permissionsactiveTab
Where is it declared?A separate manifest array of URL match patterns.A string inside the permissions array.
What sites?Hosts matching the granted patterns.The invoked tab's main-frame origin, temporarily.
When is access available?While the relevant site grant remains available.After a qualifying user invocation, until revoked.
Can it inject JavaScript?With scripting and a matching site grant.With scripting during the temporary grant.
Installation warning?Host requests can produce site-access warnings.No warning from activeTab itself.

Chrome's permission declaration guide describes host access separately from API permissions. For the other manifest fields, start with our Chrome extension permissions guide.

Two minimal Manifest V3 examples

These original examples put a teal outline around a page. Save either JSON block as manifest.json and the shared script below as worker.js in the same folder. Keep the variants in separate folders. We checked JSON syntax and ran the handler against mocked Chrome APIs on October 22, 2026. We haven't installed these examples in Chrome or tested their permission prompts there.

Example A: temporary access with activeTab

{
  "manifest_version": 3,
  "name": "Page Outline: Click Access",
  "version": "1.0.0",
  "permissions": [
    "activeTab",
    "scripting"
  ],
  "background": {
    "service_worker": "worker.js"
  },
  "action": {
    "default_title": "Outline this page"
  }
}

Example B: host permission for example.com

{
  "manifest_version": 3,
  "name": "Page Outline: Site Access",
  "version": "1.0.0",
  "permissions": [
    "scripting"
  ],
  "host_permissions": [
    "https://example.com/*"
  ],
  "background": {
    "service_worker": "worker.js"
  },
  "action": {
    "default_title": "Outline this page"
  }
}

Example A can act on an allowed page after its toolbar button is clicked. Example B requests HTTPS access to example.com. It doesn't cover www.example.com or unrelated hosts. Both use the same click handler so you can compare authorization without changing the feature.

The shared worker.js

chrome.action.onClicked.addListener(async (tab) => {
  if (tab.id === undefined) return;
  try {
    await chrome.scripting.executeScript({
      target: { tabId: tab.id },
      func: () => {
        document.documentElement.style.outline = "4px solid #00796b";
      }
    });
  } catch (error) {
    console.warn("Page access unavailable:", error.message);
  }
});

The handler runs only after a toolbar click. Host permission doesn't change that trigger. The scripting API requires its named permission plus host access, supplied here by either declaration. Injection targets the main frame by default. The catch handles denied access. it doesn't bypass it.

To compare the scopes in Chrome, use https://example.com/ as a baseline, then try an ordinary HTTPS page on another host. With site access granted, either variant should outline the baseline page after a click. On the second host, only Example A has the permission needed for that click. For Example B, inspect the worker's console for the denied injection. These are expected outcomes, not browser test results. If your result differs, check Chrome's site-access setting and the exact URL before widening the manifest.

How long does activeTab last?

Clicking an extension action is one qualifying invocation. Merely selecting a tab isn't. Chrome also supports invocation through an extension keyboard command or context-menu item, as documented in its activeTab guide.

Suppose you invoke the extension on https://example.com/article. Navigate within that origin to https://example.com/about and the grant remains available; follow a link to https://example.org/ and Chrome revokes it. Closing the tab ends it too. Don't assume a click authorizes every tab or a third-party iframe. And neither access model permits injection into restricted pages such as chrome://settings.

Host access still has boundaries

A host_permissions grant can remain available across visits to matching sites. Service workers still stop when idle. Check the user's actual grant. A declared host can still have its access withheld by the user.

Check the pattern carefully. In host permissions, the path is ignored: https://example.com/reports/* doesn't confine access to reports. Use https://example.com/* for an exact HTTPS host, or https://*.example.com/* when the base domain and its subdomains are needed. Chrome's match pattern reference explains the syntax.

Three mistakes when choosing permissions

Adding tabs won't fix missing page-injection permission.Sensitive tab metadata and access to a page's DOM are different capabilities. For an injection failure, check the target URL and the site grant before adding more API permissions.

Declaring a host won't schedule a content script either. Automatic scripts need an injection mechanism, such as content_scripts.matches. Manifest-declared content scripts don't need duplicate host_permissions for their declared injection. See Chrome's content script guide.

And activeTab on a shopping page doesn't authorize an unrelated API host. A service worker fetching from https://api.example.net/ needs appropriate access for that server. Host permissions don't remove CORS restrictions from requests made inside content scripts. Chrome's cross-origin request guide covers that difference.

Choose access for the feature you actually need

For a click-to-outline tool, we'd start with activeTab. A tool that automatically annotates a known work site needs an access and injection plan that survives ordinary browsing. An optional integration can declare optional_host_permissions and request a specific site when the user chooses that feature.

That request needs a user gesture. Check the result of chrome.permissions.request and support refusal. Use chrome.permissions.contains when checking a particular existing grant. The Permissions API reference documents both calls. Optional host access can persist after approval; it isn't the same as activeTab's temporary lifetime.

Review an existing extension's site access

Download a currently available public package with CRXFile, extract the ZIP, then read its manifest.json. The package tells you what the publisher requests, not what an installed user has granted. CRXFile retrieves packages. It doesn't certify their safety.

Follow the toolbar handler or automatic script into its DOM reads and network calls using our source inspection guide. Record the exact host, the starting event, and the data the feature uses. If a button that only changes a page's outline requests every website, ask why it needs access before anyone clicks it.