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.
Table of Contents
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.
| Feature | Approach | Scope | User Warning | Install Impact | Review Scrutiny |
|---|---|---|---|---|---|
| activeTab only | Current tab on click | None at install | Minimal | Low | |
| Specific host | One domain | "Read your data on example.com" | Moderate | Low | |
| Wildcard subdomain | All subdomains of a domain | "Read your data on *.example.com" | Moderate | Medium | |
| Multiple specific hosts | Listed domains only | "Read your data on [list]" | Varies by count | Medium | |
| <all_urls> | Every website | "Read and change all your data on all websites" | Severe — up to 50% drop | Maximum |
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.
- 📜
Manifest V1 (2010)
Broad permissions with minimal warnings. Extensions could request everything with few consequences. Users had little visibility into what extensions accessed.
- ⚡
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.
- ⚠️
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.
- 🔒
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.
- 🛡️
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.
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.
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.
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.
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.
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.
- 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
- 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#
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?
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
Manifest V3 Permissions: Best Practices
Best practices for requesting and managing permissions in Manifest V3 Chrome extensions. Covers required vs optional permissions, host permissions, and review implications.
Data Handling Compliance for Extensions
Navigate GDPR, CCPA, and Chrome Web Store data policies for your extension. Covers consent flows, data deletion, privacy manifests, and third-party data sharing rules.
Chrome Web Store Policy Changes in 2026
Stay compliant with every Chrome Web Store policy change in 2026. Privacy Sandbox impact, Manifest V3 deadlines, new disclosure rules, and action checklists.