Compliance10 min read

Host Permissions Explained: What, Why, and When

Understand Chrome extension host permissions — patterns, optional vs required, activeTab, narrowing scope, and how permissions affect user trust and store review.

C
CWS Kit Team
Share

Host permissions determine which websites your extension can read from, modify, or interact with. They are the most powerful and most scrutinized capability in the extension permission model. Request too many, and users will not install your extension. Request too few, and your extension breaks silently on the pages it needs to work on.

The challenge is that host permissions exist in a complicated hierarchy — match patterns, optional permissions, the activeTab escape hatch, content script URL patterns, and the new Manifest V3 runtime permission model all interact in non-obvious ways. This guide untangles all of it.

Permission Patterns Decoded#

Every host permission is a match pattern with the format <scheme>://<host>/<path>. Each component can use wildcards, but the rules are specific.

Here is how these patterns look in a real manifest:

{
  "manifest_version": 3,
  "name": "GitHub PR Helper",
  "host_permissions": [
    "https://github.com/*",
    "https://api.github.com/*"
  ],
  "optional_host_permissions": [
    "https://gitlab.com/*",
    "https://*.atlassian.net/*"
  ],
  "permissions": [
    "activeTab",
    "storage"
  ]
}

This manifest says: "I always need GitHub access, I might need GitLab and Jira access later, and I can activate on any tab the user clicks my icon on."

Permission Scopes Compared#

Not all permission approaches carry the same user-facing weight. The permission warning a user sees at install time directly correlates with install conversion rate.

FeatureApproachScopeUser WarningInstall ImpactReview Scrutiny
activeTab onlyCurrent tab on clickNone at installMinimalLow
Specific hostOne domain"Read your data on example.com"ModerateLow
Wildcard subdomainAll subdomains of a domain"Read your data on *.example.com"ModerateMedium
Multiple specific hostsListed domains only"Read your data on [list]"Varies by countMedium
<all_urls>Every website"Read and change all your data on all websites"Severe — up to 50% dropMaximum

The activeTab Alternative#

activeTab is the most underused permission in the extension ecosystem. It grants temporary access to the current tab — but only when the user explicitly interacts with your extension via a toolbar click, keyboard shortcut, or context menu selection.

// With activeTab, you can inject into the current tab
// ONLY after the user clicks your extension icon
 
chrome.action.onClicked.addListener(async (tab) => {
  // This works because the user just clicked — activeTab is active
  const results = await chrome.scripting.executeScript({
    target: { tabId: tab.id! },
    func: () => {
      return document.title;
    },
  });
 
  console.log("Page title:", results[0].result);
});

The beauty of activeTab is that it requires zero permission warnings at install. The user's click is the permission grant. After the user navigates away or the tab is closed, access expires automatically.

Use activeTab when your extension performs actions on demand — "translate this page," "save this article," "check accessibility." Do not use it when your extension needs to monitor pages in the background or inject content scripts automatically.

The Evolution of Permissions#

The permission model has changed significantly across manifest versions. Understanding the history explains why some patterns exist.

  1. 📜

    Manifest V1 (2010)

    Broad permissions with minimal warnings. Extensions could request everything with few consequences. Users had little visibility into what extensions accessed.

  2. Manifest V2 (2012)

    Introduced optional permissions and the activeTab permission. Permission warnings became more prominent in the install dialog. Content scripts could still declare broad URL patterns.

  3. ⚠️

    Host Permission Warnings (2018)

    Chrome began showing explicit host permission warnings at install time. Extensions requesting <all_urls> saw significant install rate drops as users became aware of the broad access.

  4. 🔒

    Manifest V3 (2020-2024)

    Host permissions moved to a separate 'host_permissions' key. Users gained the ability to restrict host access post-install via the extension's site access controls. Runtime host permissions became the recommended approach.

  5. 🛡️

    Site Access Controls (2024+)

    Chrome now lets users override extension host permissions — restricting access to 'on click', 'on specific sites', or 'on all sites' regardless of what the manifest requests. Extensions must handle denied access gracefully.

The trend is clear: each iteration gives users more control over what extensions can access. Your extension must handle permission denial gracefully — not just crash when a host permission is revoked.

Requesting Minimal Permissions#

Follow this decision framework to choose the narrowest permission scope that works.

1

Start with activeTab

If your extension only acts when the user explicitly triggers it (clicks icon, uses shortcut, right-click menu), activeTab is enough. No host permissions needed.

2

Add Specific Hosts Only If Needed

If you need background access to specific APIs (e.g., your own backend, a specific service), add only those exact domains to host_permissions.

3

Use Optional Permissions for Extras

If you support multiple services but not all users need all of them, put additional domains in optional_host_permissions and request at runtime.

4

Wildcard Subdomains Only for Platforms

Use *.example.com only for platforms that use unpredictable subdomains (e.g., *.atlassian.net, *.shopify.com). Never wildcard a TLD.

5

Justify <all_urls> Thoroughly

If you genuinely need all-site access (password manager, ad blocker, accessibility tool), prepare a detailed privacy policy and permission justification for the store listing.

Runtime Permission Requests#

For optional permissions, request them at the moment the user needs the feature — not at install time, and not at first launch.

// Request optional host permission when user enables a feature
async function enableGitLabIntegration(): Promise<boolean> {
  const granted = await chrome.permissions.request({
    origins: ["https://gitlab.com/*"],
  });
 
  if (granted) {
    // Register content scripts for GitLab
    await chrome.scripting.registerContentScripts([
      {
        id: "gitlab-integration",
        matches: ["https://gitlab.com/*"],
        js: ["content-scripts/gitlab.js"],
      },
    ]);
    await chrome.storage.local.set({ gitlabEnabled: true });
    return true;
  }
 
  // User denied — show explanation, don't pester
  return false;
}
 
// Check if permission is already granted before requesting
async function hasGitLabPermission(): Promise<boolean> {
  return chrome.permissions.contains({
    origins: ["https://gitlab.com/*"],
  });
}

The chrome.permissions.request() call must happen inside a user gesture handler (click, keypress). You cannot call it from a timer, background event, or DOMContentLoaded. Chrome silently rejects requests without a user gesture.

Permission Justification Strings#

The Chrome Web Store requires a justification for every host permission. This is not just for the review team — it is displayed to users in some contexts.

Do
  • Explain the specific feature that needs the permission: 'Access to github.com is needed to add code review annotations to pull request pages'
  • Justify why optional permissions cannot work: 'Content scripts must run automatically on page load to block ads before they render'
  • Reference your privacy policy and data handling practices
  • Use the narrowest pattern that works — reviewers notice and appreciate precision
  • Respond promptly to reviewer questions about permissions with concrete technical explanations
Avoid
  • Write vague justifications: 'needed for the extension to work properly'
  • Request permissions you might need in the future but don't use yet
  • Request <all_urls> because you support 'any website' when you actually support a known list
  • Ignore the permission justification field — empty justifications increase rejection chance
  • Request broad permissions and then filter in your code — the manifest should be the filter

Handling Permission Revocation#

Users can now revoke your host permissions from chrome://extensions or the site access popup. Your extension must survive this gracefully.

// Listen for permission changes
chrome.permissions.onRemoved.addListener((permissions) => {
  if (permissions.origins?.length) {
    console.log("Lost access to:", permissions.origins);
 
    // Disable features that depend on revoked permissions
    permissions.origins.forEach((origin) => {
      disableFeaturesForOrigin(origin);
    });
 
    // Notify user in next popup open
    chrome.storage.local.set({
      permissionWarning: `Some features are disabled because site access to ${permissions.origins.join(", ")} was revoked.`,
    });
  }
});
 
// Always check before using a permission
async function safeInjectScript(tabId: number, url: string): Promise<void> {
  const hasPermission = await chrome.permissions.contains({
    origins: [new URL(url).origin + "/*"],
  });
 
  if (!hasPermission) {
    // Show UI explaining why the feature is unavailable
    showPermissionRequired(tabId, url);
    return;
  }
 
  await chrome.scripting.executeScript({
    target: { tabId },
    files: ["content-script.js"],
  });
}

Test Your Permission Knowledge#

Knowledge Check

1. What permission should you use if your extension only needs to access the current tab when the user clicks the toolbar icon?

2. Which match pattern is INVALID?

3. When can chrome.permissions.request() be called successfully?

4. What happens when a user revokes your extension's host permission for a site?

Key Takeaway
Start every extension with activeTab and prove you need more. Use specific hosts in host_permissions for domains you always need, optional_host_permissions for domains you sometimes need, and reserve <all_urls> for extensions that genuinely operate on every website. Always handle permission revocation — users now have post-install control over your access. Write detailed justifications for every host permission to speed up Chrome Web Store review.

Permissions intersect with nearly every aspect of extension development. For a deeper look at how the store reviews permission requests, see Chrome Web Store review process. If you are migrating from Manifest V2, the complete MV3 guide covers how host permissions moved from the permissions array to the dedicated host_permissions key.

Continue reading

Related articles

View all posts