The Chrome Extensions platform has undergone a significant transformation with the introduction of Manifest V3 (MV3), representing Google’s vision for a more secure, private, and performant extension ecosystem. This evolution from Manifest V2 (MV2) marks one of the most substantial changes to the extension architecture since its inception. Manifest V3 is not merely an update but a fundamental shift in how extensions interact with the browser and web content, designed to address critical concerns around security, performance, and user privacy while maintaining the extensibility that makes Chrome powerful. n
The transition to MV3 comes as part of Google’s broader effort to enhance the security model of extensions, giving users greater control and transparency over what extensions can do while reducing their potential negative impact on browser performance. According to Chrome’s development team, MV3 aims to “improve the privacy, security, and performance of extensions” while “giving users more understanding and control over what extensions are capable of”. This change aligns with similar initiatives across the software industry toward greater privacy protections and more efficient resource utilization. n
For developers, understanding and implementing this migration is crucial, as Manifest V2 support is being phased out. Google began disabling MV2 extensions in pre-stable versions of Chrome (Dev, Canary, and Beta) as early as June 2024, with a complete phase-out expected in the stable channel following a gradual rollout. Enterprises have until June 2025 to complete their migration when using specific enterprise policies. n
Key Changes in Manifest V3
Service Workers Replace Background Pages
One of the most significant architectural changes in MV3 is the replacement of background pages with service workers. In Manifest V2, extensions used a persistent background page that remained running continuously, consuming system resources even when not actively needed. MV3 introduces service workers that are event-based and terminate when not in use, significantly reducing memory and CPU consumption.
Service workers differ from background pages in several important ways. They don’t have DOM access, which means developers need to use alternative APIs for tasks that previously required DOM manipulation. Additionally, service workers have different lifecycle patterns—they will run when needed and terminate when idle, which means developers cannot rely on long-lived global variables. This change encourages more efficient programming patterns and reduces the resource footprint of extensions.
No Remotely Hosted Code
Manifest V3 introduces strict restrictions on executing remote code, a significant security enhancement. Extensions can no longer execute JavaScript or WASM files retrieved from remote servers unless explicitly allowed through documented APIs. This change ensures that all code executed by an extension has been reviewed through the Chrome Web Store submission process, reducing the risk of malicious code execution after installation.
The policy allows extensions to load external data but not executable logic. Common violations include using <script>
tags pointing to external resources, executing strings fetched from remote sources using eval()
or similar functions, and building interpreters to run complex commands fetched remotely. Exemptions exist for certain contexts like iframes and sandboxed pages, but these still require compliance with data policies.
Declarative Net Request API
The declarativeNetRequest API replaces the blocking version of the webRequest API for modifying network requests. This change addresses performance and privacy concerns associated with the previous approach, where extensions needed to intercept and potentially proxy all network traffic to provide filtering capabilities.
The new API allows extensions to declare rules for how network requests should be handled without requiring them to intercept every request. This approach reduces the need for excessive permissions and minimizes the extension’s ability to access sensitive user data. Google has increased the limits for static and dynamic rules in this API based on developer feedback, making it more capable for content filtering use cases.
Enhanced Security Policies
Manifest V3 introduces stricter Content Security Policy (CSP) requirements for extensions. The CSP now must be structured as an object with specific directives for different contexts rather than a simple string. Notably, MV3 no longer supports 'unsafe-eval'
in CSP for extension pages, preventing the execution of arbitrary strings as code.
These security enhancements work together to create a more robust security model that protects users from malicious extensions while still allowing legitimate functionality. The changes also make it easier to review extensions before they’re published on the Chrome Web Store, as all executable code must be included in the extension package.
Step-by-Step Migration Process
1. Updating the Manifest File
The first step in migrating to Manifest V3 is updating the manifest.json
file. The most obvious change is updating the manifest_version
field:
{
// Manifest V2
"manifest_version": 2,
// Manifest V3
"manifest_version": 3
}
Beyond this version change, several other structural updates are required. The browser_action
and page_action
properties are unified into a single action
property:
{
// Manifest V2
"browser_action": {
"default_popup": "popup.html",
"default_icon": "icon.png"
},
// Manifest V3
"action": {
"default_popup": "popup.html",
"default_icon": "icon.png"
}
}
The background section must be updated to use a service worker instead of background scripts or pages:
{
// Manifest V2
"background": {
"scripts": ["background.js"]
},
// Manifest V3
"background": {
"service_worker": "background.js"
}
}
n The web_accessible_resources
property requires a more detailed structure in MV3:
{
// Manifest V2
"web_accessible_resources": [
"resources/script.js",
"resources/style.css"
],
// Manifest V3
"web_accessible_resources": [{
"resources": ["resources/script.js", "resources/style.css"],
"matches": ["https://example.com/*"],
"extension_ids": []
}]
}
Content Security Policy declaration also changes significantly:
{
// Manifest V2
"content_security_policy": "script-src 'self'; object-src 'self'",
// Manifest V3
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'",
"sandbox": "sandbox allow-scripts; script-src 'self'"
}
}
Key Manifest.json Changes from MV2 to MV3:
| Component | Manifest V2 | Manifest V3 | Notes |
|:—:|:—:|:—:|:—:|
| Manifest Version | “manifestversion”: 2 | “manifestversion”: 3 | Required change |
| Background | “scripts”: [“bg.js”] | “serviceworker”: “sw.js” | Single file only |
| Browser action | “browseraction”: {…} | “action”: {…} | Unified action |
| Web accessible resources | Array of strings | Object with resources, matches | More structured |
| Content Security Policy | String | Object with directives | Tighter restrictions |
2. Migrating to Service Workers
Converting background pages to service workers requires careful attention to lifecycle management and state persistence. Unlike persistent background pages, service workers terminate when idle, so you cannot rely on global variables to maintain state between operations.
For state management, migrate from global variables to the chrome.storage
API:
// Manifest V2 (background page)
let userData = {};
// Manifest V3 (service worker)
await chrome.storage.local.set({userData: data});
const result = await chrome.storage.local.get('userData');
If your background script currently consists of multiple files, you need to bundle them into a single service worker file or use the importScripts()
method:
// In your service worker file
importScripts('script1.js', 'script2.js', 'script3.js');
However, note that using importScripts
has limitations and may not work seamlessly with all build systems. For complex extensions using tools like Webpack, you may need to configure your build process to output a single bundled file for the service worker.
For operations requiring DOM access, which service workers don’t provide, you’ll need to use the Offscreen Documents API. This allows you to create and manage offscreen documents for specific DOM-related tasks:
// Create an offscreen document for DOM manipulation
await chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: ['DOM_PARSING'],
justification: 'Parse HTML content'
});
3. Updating API Calls and Permissions
Manifest V3 introduces changes to many extension APIs. The most significant change is the replacement of the blocking webRequest API with the declarativeNetRequest API for modifying network requests.
Instead of intercepting and modifying requests programmatically:
// Manifest V2 (webRequest API)
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
return {cancel: true};
},
{urls: ["https://example.com/*"]},
["blocking"]
);
You now declare rules in the manifest and implement them through the declarativeNetRequest API:
// Manifest V3 (declarativeNetRequest API)
// First, declare rules in manifest.json
"declarative_net_request" : {
"rule_resources" : [{
"id": "ruleset_1",
"enabled": true,
"path": "rules.json"
}]
}
// Then, in your service worker or other scripts
chrome.declarativeNetRequest.updateDynamicRules({
addRules: [{
id: 1,
priority: 1,
action: { type: 'block' },
condition: { urlFilter: 'https://example.com/*', resourceTypes: ['main_frame'] }
}],
removeRuleIds: [1]
});
Additionally, review your permission declarations as some permissions have changed or been renamed. The "activeTab"
permission remains available but may require different implementation approaches in MV3.
4. Implementing New Features and APIs
Manifest V3 introduces several new APIs and capabilities that developers should leverage. The Side Panel API allows extensions to host content in the browser’s side panel alongside the main content of a web page. The Offscreen API enables extensions to use DOM APIs in a hidden document without interrupting the user experience.
Another valuable addition is improved support for promise-based methods in many Chrome extension APIs. While callbacks are still supported, promises to provide a more modern and readable approach to asynchronous programming:
// Using promises instead of callbacks
// Old approach (callbacks)
chrome.storage.local.get('key', function(result) {
console.log(result.key);
});
// New approach (promises)
const result = await chrome.storage.local.get('key');
console.log(result.key);
Challenges and Solutions in Migration
Adapting to Service Worker Limitations
The transition from background pages to service workers presents one of the most significant challenges for developers migrating to MV3. Service workers have different lifecycle patterns than background pages—they start when needed and terminate when idle, which means they cannot maintain persistent state in global variables.
To address this challenge, developers should:
- Use storage APIs for state persistence: Replace global variables with
chrome.storage.local
orchrome.storage.session
for storing state that needs to persist between service worker restarts. - Implement heartbeat mechanisms: For extensions that need to perform regular activities, implement a heartbeat using
chrome.alarms
or periodic events to keep the service worker active when necessary. - Leverage offscreen documents: For tasks requiring DOM access, use the Offscreen API to create and manage hidden documents.
Alternatives to Remotely Hosted Code
The prohibition on remotely hosted code requires developers to find alternative approaches for functionality that previously relied on dynamic code loading. Instead of fetching and executing remote code, consider these approaches:
- Remote configuration: Fetch configuration data from remote servers and use logic contained within your extension to interpret this configuration:
// Fetch configuration rather than code
const response = await fetch('https://api.example.com/configuration');
const config = await response.json();
// Use local logic to implement functionality based on config
if (config.featureEnabled) {
await localImplementation();
}
- External communication: Use message passing to communicate with external services through documented APIs:
// Communicate with external services via messaging
chrome.runtime.sendMessage(
{type: 'fetch_data', payload: {url: 'https://api.example.com/data'}},
function(response) {
processData(response.data);
}
);
-
Regular updates: Since you can’t change functionality remotely, plan for more frequent extension updates when functionality needs to change.
Content Filtering with Declarative Net Request
For content blocking extensions, transitioning from the webRequest API to the declarativeNetRequest API requires a significant architectural shift. The declarative approach has different capabilities and limitations compared to the imperative webRequest API.
To maximize effectiveness with the declarativeNetRequest API:
-
Understand rule limitations: Be aware of the limits on the number of static and dynamic rules and design your rule sets accordingly.
-
Use dynamic rules strategically: Reserve dynamic rules for user-defined filters or frequently updated rules, while using static rules for core filtering requirements.
-
Leverage all available rule conditions: all available matching conditions, including resource types, domains, and URL patterns, to create precise rules.
Timeline and Enforcement
Google has outlined a phased timeline for the deprecation of Manifest V2 extensions. The process began in June 2024 with the disabling of MV2 extensions in pre-stable versions of Chrome (Dev, Canary, and Beta) in Chrome 127 and later. Users impacted by this rollout see MV2 extensions automatically disabled in their browser and can no longer install MV2 extensions from the Chrome Web Store.
The rollout will gradually expand to the stable channel of Chrome following a period of observation and stabilization in pre-stable channels. The exact timing for the stable channel rollout may vary based on collected data and user feedback.
Enterprise users have additional time for migration. Organizations using the ExtensionManifestV2Availability policy have until June 2025 to migrate their MV2 extensions. This extended timeline acknowledges the additional complexity enterprises face in deploying and managing extension updates across large organizations.
Microsoft Edge, being based on Chromium, generally follows a similar timeline for Manifest V2 deprecation, though the exact dates may differ. The Microsoft Edge team has stated they will “independently decide on MV3 migration timelines for Microsoft Edge extensions”.
Best Practices for a Successful Migration
Incremental Migration Approach
Rather than attempting a complete rewrite, adopt an incremental migration approach where possible. Start by updating the manifest file to MV3 while maintaining most of your existing code structure. Then gradually migrate individual components, testing thoroughly at each step.
Google recommends not adding new functionality when migrating to reduce the chances of unexpected issues or bugs. Adding features that require new permissions may trigger permission warnings, which will disable your extension until the user accepts the new permissions.
Testing and Validation Strategies
Comprehensive testing is crucial for a successful migration. Implement a structured testing strategy that includes:
- Functionality testing: Verify that all existing features work correctly under MV3.
- Performance testing: Measure memory and CPU usage to ensure the service worker-based implementation provides the expected performance benefits.
- Cross-version testing: Test your extension on different Chrome versions to ensure compatibility, especially if using features introduced after Chrome 88.
- User acceptance testing: For published extensions, perform a staged rollout to a limited audience before making the update available to all users.
Utilizing Available Resources and Tools
Take advantage of the resources and tools provided by Google to assist with migration:
- Extension Manifest Converter: This tool helps convert manifest files from V2 to V3, though it doesn’t handle all migration tasks.
- Migration documentation: The official migration guide provides detailed information on all aspects of the migration process.
- API reference documentation: The Chrome Extensions API reference includes support information for individual API members.
- Developer communities: Engage with other developers through communities like the Chromium Extensions group to share experiences and solutions.
The migration from Manifest V2 to Manifest V3 represents a significant shift in the Chrome Extensions ecosystem, bringing substantial benefits in security, privacy, and performance. While the migration process requires effort and adaptation to new patterns and APIs, the long-term benefits justify this investment.
By following a systematic approach to migration—updating the manifest, converting to service workers, replacing deprecated APIs, and thoroughly testing the changes—developers can successfully navigate this transition. The key is to start early, leverage available resources, and embrace the new event-based, service-oriented architecture that Manifest V3 promotes.
As the web platform continues to evolve, Manifest V3 provides a foundation for extensions that are more secure, performant, and respectful of user privacy. By completing this migration, developers ensure their extensions remain available to users while taking advantage of the latest capabilities in the Chrome Extensions platform.
Important sources
- Official Migration Guide
- Manifest V3 Overview
- Chrome Extensions API Reference
- Chromium Extensions Group