Anyone can unzip a published extension and read everything inside it. Whether you are allowed to ship an obfuscated one depends on the store.
A published extension is a downloadable copy of your source
A published extension is a downloadable copy of your source. The package a store serves is a zip file, and unpacking one is a right click. The background script, the content scripts and any bundled logic are then ordinary files on disk. Anyone can read the interesting part, lift it, and reupload a fork under another name.
Check the store policy first
Store policy decides whether you can ship an obfuscated extension at all. The restrictions differ by store and they change, and a reviewer may require you to submit readable or original sources alongside the package you upload. Read your store's current policy before you ship an obfuscated build, and plan for a source archive being asked for.
Obfuscation is not a way to get a build through review or to keep what your extension does away from a reviewer. If a store asks what the code does, answer that question.
What an obfuscated extension build changes
Where an obfuscated build is allowed, the output fits what the platform requires of it. It runs without eval, which an extension's content-security-policy forbids.
Every build draws a fresh seed and comes out in a different shape. A fork or a patch someone built against one version of your package does not carry over to the next version you publish.
Run it over the packed output
Run the CLI over your built output directory as a post-build step, or use the AfterPack plugin for your bundler when one is wired in:
$ npx afterpack@latest dist/ --preset=hardOnly .js, .mjs and .cjs files are collected, so manifest.json and anything under _locales/ passes through untouched. Use paths.exclude for a vendored SDK you want left alone, and paths.include to reach a node_modules/ copy bundled into the package. If a symbol name has to stay the same across your own files, pin it with identifiers.reserved.
Build every context in one run, background service worker and content scripts together, so they share a seed and the message types both sides depend on stay in agreement.
Check the package before you zip it
Open the Protection Map after a build to confirm the scripts you meant to protect were covered. Keep sourceMap.enabled off for the package you upload, since a map inside the zip is as complete a source read as shipping the original files. Run afterpack verify on the directory you are about to zip, so a build where protection did not apply fails before it reaches the store.
What this does not stop for an extension
A new shape on every release raises the cost of keeping a fork or a patch current with your extension, and that cost is paid again each time you publish.
It does not stop anyone reading, forking or reuploading the package, and a key or token inside the extension is not made safe by being encoded. What to do about an API key you must ship covers that case.
Next
- CLI reference: running AfterPack as a post-build step.
- Quickstart: a first build before you have a pipeline.
- Shipped API keys: what obfuscation does and does not do for a credential in the bundle.