Chrome ExtensionsManifest V3UpdatesDevOps

How to Get Chrome Extension Updates to Users Faster

Published · Updated · 10 min read · By the Optymized team

You ship a critical bugfix to the Chrome Web Store. It passes review in a few hours. And then... nothing. Your users are still running the old version. Some for hours, some for days. Chrome's auto-update mechanism is a black box, and if your manifest triggers a permission re-consent, the update can stall indefinitely.

After shipping 100+ extension versions for Panel Pro, we learned exactly which manifest settings slow down updates and which speed them up. This guide covers everything: permission scoping, content script configuration, and how to build real-time version checking so users know about updates the moment they land.

How Chrome extension updates actually work

Chrome checks for extension updates roughly every 5 hours. When it finds a new version in the Chrome Web Store, it downloads and installs it silently in the background. The user never sees a prompt — unless you changed permissions.

That "unless" is where most problems start. If your new version requests permissions or host access that the previous version did not have, Chrome will:

  1. Disable the extension immediately
  2. Show a badge on the puzzle icon in the toolbar
  3. Wait for the user to manually click "Re-enable" and accept the new permissions

Until the user acts, they are stuck on the old version with the extension disabled. For a business-critical tool like an ads management extension, this is unacceptable.

1. Scope your host_permissions tightly

The host_permissions field in manifest.json is the number one cause of update friction. Every new origin you add triggers the re-consent flow.

Bad — too broad

"host_permissions": [
  "<all_urls>"
]

This requests access to every website. It also means any future narrowing of scope will never trigger a re-consent (you started at the maximum), but Google's review will be slower and users may not install at all.

Good — specific origins

"host_permissions": [
  "https://ads.allegro.pl/*",
  "https://api.example.com/*"
]

Only the origins you actually need. If you later need https://analytics.allegro.pl/*, that will trigger a re-consent — but you can plan for that.

Best — plan ahead

"host_permissions": [
  "https://*.allegro.pl/*",
  "https://api.example.com/*"
]

Use a wildcard subdomain for platforms where you know you will need multiple subdomains. Adding analytics.allegro.pl later will not trigger re-consent because it is already covered by *.allegro.pl.

2. Content scripts and match patterns

Content scripts have their own matches field that determines which pages they run on. Changing these patterns does not trigger a permission re-consent — but getting them wrong still delays your updates in other ways.

Match patterns that cause problems

"content_scripts": [{
  "matches": ["<all_urls>"],
  "js": ["content.js"]
}]

Running on every page triggers a slower review from Google. It also increases the chance of conflicts with other extensions and makes your extension look suspicious to users.

Match patterns done right

"content_scripts": [{
  "matches": [
    "https://ads.allegro.pl/*",
    "https://allegro.pl/ads/*"
  ],
  "js": ["content.js"],
  "run_at": "document_idle"
}]

Specific match patterns. Google reviews these faster because they can verify your extension only touches the pages it claims to need.

Use run_at wisely

The run_at field does not affect update speed directly, but it matters for user experience after an update:

  • document_idle (default) — safest, runs after the page is loaded
  • document_end — runs after DOM is ready but before subresources
  • document_start — runs before anything else, can slow down the page

Use document_idle unless you have a specific reason not to. Extensions that slow down page loads get lower ratings and more uninstalls.

3. The permissions field — plan your API surface upfront

The permissions array (separate from host_permissions) controls access to Chrome APIs like storage, tabs,alarms, and notifications. Adding a new permission here also triggers re-consent.

Practical advice

  • Request everything you will need in v1. If you know you will add notifications in a month, add notifications to permissions now. An unused permission is better than a re-consent that disables your extension.
  • Use optional_permissions for features you might not ship. These are requested at runtime via chrome.permissions.request() and never trigger a re-consent on update.
  • Never remove and re-add a permission. Removing tabs in v2 and adding it back in v3 will trigger re-consent in v3.

optional_permissions example

{
  "permissions": ["storage", "alarms"],
  "optional_permissions": ["notifications", "downloads"],
  "optional_host_permissions": ["https://analytics.example.com/*"]
}

Users grant optional permissions when they first use a feature that needs them. No re-consent on update, no disabled extension, no lost users.

4. Notify users in real time with version checking

Even if you do everything right with permissions and match patterns, Chrome's ~5-hour update cycle means users can be running stale code for hours. For critical bugfixes or features, that is too slow.

The solution: check the published version from inside your extension and prompt the user to update when a new version is available.

Why not a GitHub Action?

You might think CI/CD is the right place for version checking. We have a GitHub Action for permission escalation detection and it works great for catching problems before they ship. But for notifying end users about available updates, CI/CD does not help:

  • A GitHub Action runs in your pipeline, not in the user's browser
  • It burns CI minutes on every check
  • It cannot tell the user "hey, click Update now"

What you need is a lightweight API that your extension can call at runtime to compare its own version against the version published in the Chrome Web Store.

Chrome Extension Version API

We built and open-sourced chrome-extension-version-api — a single-endpoint .NET service that queries the Chrome Web Store for the published version of any extension. It runs as a Docker container with built-in response caching.

API usage

GET /check-published-extension-version/{extensionId}

// Response (200 OK)
{
  "version": "1.23.4",
  "cached": false,
  "checkedAt": "2026-03-16T12:00:00Z"
}

Deploy with Docker

docker run -p 8080:8080 ghcr.io/toumash/chrome-extension-version-api:latest

Deploy it behind your existing ingress or reverse proxy. It needs no database, no configuration, and barely any resources. Set CacheTtlMinutes to control how often it actually hits the Chrome Web Store (default: 5 minutes).

Extension-side implementation

In your content script — the code that runs on the page your users are already working on — check the published version and show an update notification directly in the UI:

// content.js — runs on the page the user is actively using
const EXTENSION_ID = chrome.runtime.id;
const VERSION_API = 'https://your-api.example.com';

async function checkForUpdate() {
  const localVersion = chrome.runtime.getManifest().version;
  const res = await fetch(
    `${VERSION_API}/check-published-extension-version/${EXTENSION_ID}`
  );
  const { version: storeVersion } = await res.json();

  if (storeVersion && storeVersion !== localVersion) {
    // Show an update banner on the page the user is working on
    const banner = document.createElement('div');
    banner.textContent = `Update available (v${storeVersion}). Please reload.`;
    banner.style.cssText =
      'position:fixed;bottom:16px;right:16px;padding:12px 20px;' +
      'background:#f97316;color:#fff;border-radius:8px;z-index:99999;' +
      'font-family:sans-serif;cursor:pointer;';
    banner.onclick = () => location.reload();
    document.body.appendChild(banner);
  }
}

// Check on page load
checkForUpdate();

The user sees a "NEW" badge on your extension icon and knows to update. You can also show a banner inside your popup or content script UI.

Why this beats waiting for Chrome

~5h

Chrome's default update check interval

30min

Your version API check interval

0

CI minutes burned

FAQ

How often does Chrome check for extension updates?

Approximately every 5 hours, but the actual interval varies. Chrome may batch update checks and network conditions can add delay. You cannot control this interval from your extension code, but you can build your own version-checking mechanism (see section 4 above) to notify users faster.

Can I force a Chrome extension to update immediately?

Users can go to chrome://extensions, enable Developer Mode, and click "Update". For a better approach, use a version-checking API to detect new versions and notify users with a badge or in-app prompt.

Do permission changes slow down updates?

Yes. If your new version adds permissions or host_permissions that the previous version did not have, Chrome disables the extension and shows a re-consent dialog. The extension stays disabled until the user manually approves.

Why not just use <all_urls> to avoid future re-consent?

While <all_urls> avoids future re-consent for host permissions, it triggers a slower Chrome Web Store review, scares users during installation, and may get your extension flagged. Use wildcard subdomains (e.g. *.example.com) for the platforms you target instead.

Related articles

Need help shipping updates faster?

We have shipped 100+ extension versions and built the tooling to make updates frictionless. Whether you need help with manifest configuration, CI/CD, or real-time version checking, we can help.

Who's behind this

Tomasz Dłuski

Tomasz Dłuski

Founder & CEO

Senior Software Engineer with 10+ years of experience. Previously part of a company that scaled from 5 to 50+ engineers. Now building Optymized — a company that combines enterprise project delivery experience with own SaaS products. Maintainer of CRXJS (3.9k GitHub stars), one of the most popular tools for building browser extensions.

Let's discuss your project

Whether you need a custom browser extension, a dedicated dev team, or technical consulting — let's find the best approach together.

or send us a message