Skip to content

Distribution

Trade-offs between zip handoff, Web Store upload, and self-hosted .crx with update_url, plus .pem key handling and how chrome’s external-.crx policy is worked around. Build-and-package already produced dist/ and packages/extension.zip; this section assumes those exist.

build-and-package is where automation stops. Channel choice, .pem key custody, hosting domain, and store-review correspondence all sit outside the tool. The .pem key and the billing or review accounts are assets that must move under your identity; if automation steps in on those, key leaks and policy incidents follow. From here on you drive.

PathRecipient setupAuto-updateReviewBest fit
Zip handoffUnzip, then Load unpacked from chrome://extensionsNoneNoneOne or two coworkers, internal PoC
Web Store uploadOne click from the storeYes (store handles it)Yes, days to weeksGeneral release
Self-hosted .crx + update_urlInstall once, auto from then onYes (update update.xml)NoneInternal distribution, controlled user base

The three paths are not exclusive. The same extension can ship through self-host internally and the Web Store for the general public at the same time. Migrating off one path onto another is also common.

Send the packages/extension.zip that pnpm run package produced. The recipient unzips it, opens chrome://extensions, turns on Developer mode, and points “Load unpacked” at the folder. Updates are a fresh zip and the same steps over again. No automatic refresh.

Fine for a PoC. The recipient repeats the same dance every time, though. Once the user count climbs past five, move to self-host.

What you need before uploading is short.

  • A consistent icon set at 16, 32, 48, and 128, usually under public/icons/.
  • Store listing screenshots at 1280×800 or 640×400.
  • A short description and a long description. Justify each permission. host_permissions is rejection reason number one.
  • A private listing option. You can release to a small group of testers before going public.

Screenshot capture and store-listing copy are not the expert seat. They want you in front of the screen, step by step. The same topic, paced differently, sits in the Beginner manual’s distribution page.

The most hands-on path, and the right one when you need control over who installs and when.

The first time chrome packs your extension into a .crx, it drops a .pem key alongside it. That key is your extension’s identity. Only .crx files signed with the same key are recognized as the same extension. Lose the key and the same extension can no longer be updated; every user has to reinstall as a brand new extension.

The key never goes into git. Keep it in 1Password or a team secret store, and write the rotation policy into an ADR. docs/adr/ already has a page for the enterprise policy decision and the key rotation procedure.

Add update_url to the manifest.

"update_url": "https://example.com/extension/update.xml"

update.xml follows the format chrome expects.

<?xml version='1.0' encoding='UTF-8'?>
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<app appid='YOUR_EXTENSION_ID'>
<updatecheck codebase='https://example.com/extension/extension-1.2.0.crx' version='1.2.0' />
</app>
</gupdate>

For each release, upload the new .crx to the same host under a fresh path and update version and codebase in update.xml. Chrome polls update_url about once a day and picks up the new version on its own. The host can be GitHub Pages, S3, or your own domain — anything that serves static files.

By default, chrome blocks .crx files that did not come from the Web Store. For internal distribution there are two ways out.

  • Register the extension ID and update_url in enterprise policy under ExtensionInstallAllowlist or ExtensionInstallForcelist. Windows uses the registry, macOS uses a plist, Linux uses JSON under /etc/opt/chrome/policies/managed/.
  • Have users install unpacked. Auto-update is off in this case.

ID registration and the host domain are expensive to change later, which is why an ADR captures that decision too. Which domain hosts update.xml and how the key rotation is scheduled both live there.

That closes the chrome-ext-base expert tier. Intro named the three guardrails, architecture covered dependency direction and message routing, build-and-package showed how dist/ and the zip come out, and this section split the three paths to the user.

Step-by-step walkthroughs, screenshots, and OS-specific install branches for non-developer readers live in the Beginner manual, where the same topics are paced differently.