Manifest V3
When to Use
- Starting a new browser extension project
- Migrating an existing MV2 extension to MV3
- Configuring permissions, background service workers, or declarative net request rules
- Debugging "service worker terminated" issues
Core Jobs
1. Manifest Structure
Required fields:
{
"manifest_version": 3,
"name": "My Extension",
"version": "1.0.0",
"description": "...",
"permissions": [],
"host_permissions": [],
"background": {
"service_worker": "background.js",
"type": "module"
},
"action": {
"default_popup": "popup.html",
"default_icon": "icons/icon48.png"
},
"icons": { "16": "icons/icon16.png", "48": "icons/icon48.png", "128": "icons/icon128.png" },
"content_scripts": [],
"web_accessible_resources": []
}
2. Permissions Design
- Declare ONLY what you need (Chrome Web Store rejects over-permissioned extensions)
permissions = extension APIs (storage, tabs, contextMenus, alarms, notifications)
host_permissions = website access (*://*.example.com/* or <all_urls>)
- Use
optional_permissions for features users might not need
- Dangerous permissions requiring justification:
<all_urls>, webNavigation, history, bookmarks
3. Service Worker (MV3 Background)
Key differences from MV2 background pages:
- Service worker TERMINATES when idle (no persistent state in memory)
- Use
chrome.storage (not global variables) to persist data
- Register event listeners at top level (not inside callbacks)
- Keep-alive pattern for long-running tasks:
chrome.alarms API
// ✅ Correct — top-level listener
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// handle message
return true; // keep channel open for async response
});
// ❌ Wrong — listener inside async callback
chrome.tabs.query({}, (tabs) => {
chrome.runtime.onMessage.addListener(...); // never registered reliably
});
4. declarativeNetRequest (replaces webRequest)
- MV3 cannot block requests dynamically with webRequest
- Use
declarativeNetRequest for URL blocking/redirecting
- Rules defined in JSON files and declared in manifest
{
"declarative_net_request": {
"rule_resources": [{ "id": "ruleset_1", "enabled": true, "path": "rules.json" }]
}
}
5. Content Security Policy
- MV3 enforces strict CSP: no inline scripts, no
eval()
- Remote code execution prohibited (no loading scripts from CDN at runtime)
- All scripts must be bundled in the extension package
- Use
web_accessible_resources for resources injected into pages
Key Concepts
- Manifest V3 — current extension standard; MV2 deprecated Jan 2025 in Chrome
- Service worker — event-driven background script that terminates when idle
- host_permissions — controls which websites the extension can access
- optional_permissions — permissions requested at runtime (better UX)
- declarativeNetRequest — static rules for network request modification (replaces dynamic webRequest)
- web_accessible_resources — extension files accessible from web pages
Checklist
Output Format
- 🔴 Critical — MV2 manifest, remote code execution, missing required fields
- 🟡 Warning — overly broad host_permissions (
<all_urls>), persistent state in service worker
- 🟢 Suggestion — use optional_permissions for non-core features
Common Pitfalls
- Service worker termination: store state in
chrome.storage.session or chrome.storage.local, not global vars
return true in onMessage listener is required to keep the message channel open for async responses
- MV3 blocks all inline scripts — use external .js files even for tiny scripts
web_accessible_resources must explicitly list files injected into pages
1---2name: manifest-v33description: Design and configure Manifest V3 browser extensions — service workers, permissions, declarative rules, and migration from MV2.4---56# Manifest V378## When to Use9- Starting a new browser extension project10- Migrating an existing MV2 extension to MV311- Configuring permissions, background service workers, or declarative net request rules12- Debugging "service worker terminated" issues1314## Core Jobs1516### 1. Manifest Structure17Required fields:18```json19{20 "manifest_version": 3,21 "name": "My Extension",22 "version": "1.0.0",23 "description": "...",24 "permissions": [],25 "host_permissions": [],26 "background": {27 "service_worker": "background.js",28 "type": "module"29 },30 "action": {31 "default_popup": "popup.html",32 "default_icon": "icons/icon48.png"33 },34 "icons": { "16": "icons/icon16.png", "48": "icons/icon48.png", "128": "icons/icon128.png" },35 "content_scripts": [],36 "web_accessible_resources": []37}38```3940### 2. Permissions Design41- Declare ONLY what you need (Chrome Web Store rejects over-permissioned extensions)42- `permissions` = extension APIs (storage, tabs, contextMenus, alarms, notifications)43- `host_permissions` = website access (`*://*.example.com/*` or `<all_urls>`)44- Use `optional_permissions` for features users might not need45- Dangerous permissions requiring justification: `<all_urls>`, `webNavigation`, `history`, `bookmarks`4647### 3. Service Worker (MV3 Background)48Key differences from MV2 background pages:49- Service worker TERMINATES when idle (no persistent state in memory)50- Use `chrome.storage` (not global variables) to persist data51- Register event listeners at top level (not inside callbacks)52- Keep-alive pattern for long-running tasks: `chrome.alarms` API5354```javascript55// ✅ Correct — top-level listener56chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {57 // handle message58 return true; // keep channel open for async response59});6061// ❌ Wrong — listener inside async callback62chrome.tabs.query({}, (tabs) => {63 chrome.runtime.onMessage.addListener(...); // never registered reliably64});65```6667### 4. declarativeNetRequest (replaces webRequest)68- MV3 cannot block requests dynamically with webRequest69- Use `declarativeNetRequest` for URL blocking/redirecting70- Rules defined in JSON files and declared in manifest7172```json73{74 "declarative_net_request": {75 "rule_resources": [{ "id": "ruleset_1", "enabled": true, "path": "rules.json" }]76 }77}78```7980### 5. Content Security Policy81- MV3 enforces strict CSP: no inline scripts, no `eval()`82- Remote code execution prohibited (no loading scripts from CDN at runtime)83- All scripts must be bundled in the extension package84- Use `web_accessible_resources` for resources injected into pages8586## Key Concepts87- **Manifest V3** — current extension standard; MV2 deprecated Jan 2025 in Chrome88- **Service worker** — event-driven background script that terminates when idle89- **host_permissions** — controls which websites the extension can access90- **optional_permissions** — permissions requested at runtime (better UX)91- **declarativeNetRequest** — static rules for network request modification (replaces dynamic webRequest)92- **web_accessible_resources** — extension files accessible from web pages9394## Checklist95- [ ] `manifest_version: 3` (not 2)?96- [ ] No inline scripts (CSP compliant)?97- [ ] No remote code execution (all scripts bundled)?98- [ ] Service worker uses `chrome.storage` not in-memory state?99- [ ] Event listeners registered at top level (not in callbacks)?100- [ ] Minimal permissions — only what's needed?101- [ ] `host_permissions` scoped as narrowly as possible?102- [ ] Icons at 16, 48, 128px?103104## Output Format105- 🔴 **Critical** — MV2 manifest, remote code execution, missing required fields106- 🟡 **Warning** — overly broad host_permissions (`<all_urls>`), persistent state in service worker107- 🟢 **Suggestion** — use optional_permissions for non-core features108109## Common Pitfalls110- Service worker termination: store state in `chrome.storage.session` or `chrome.storage.local`, not global vars111- `return true` in `onMessage` listener is required to keep the message channel open for async responses112- MV3 blocks all inline scripts — use external .js files even for tiny scripts113- `web_accessible_resources` must explicitly list files injected into pages