# Announcing AfterPack: a free JavaScript obfuscator for the web

> Source: <https://dev.to/nikitaeverywhere/announcing-afterpack-a-free-javascript-obfuscator-for-the-web-1g85>
> Published: 2026-10-03 00:15:32+00:00

*I built AfterPack; this is the launch post from our blog.*

Today we're launching three things:

AfterPack (afterpack.dev), the JavaScript obfuscator, works with any framework. The CLI and plugins are open source, the local engine is free, and Pro cloud builds start at $49 a month. Start with `npx afterpack@latest` after a build, or ask your coding agent to add it.

Meet AfterPack: a free build tool that makes shipped code unreadable to scanners and people, and a moving target for AI. Every build ships completely different code, so a script written to patch one release — a cheat, a userscript, a bypass — doesn't fit the next. A tool written against your code should stop working at your next release, and now, inside your own Cloudflare Worker, it can stop working at your very next request.

Because whatever AI can read, it can also rewrite and repackage. Point a coding agent at your bundle, and a few minutes later someone holds your pricing rules, your paywall check or your anti-bot logic as clean, editable source, along with a script that patches or bypasses them. That used to be a specialist's job; now it's a prompt. And the patch keeps working for as long as your code keeps its shape. Minification was never hiding that code: the [Claude Code "leak"](https://www.afterpack.dev/blog/claude-code-source-leak?utm_source=devto&utm_medium=social&utm_campaign=launch) was a readable bundle that had been sitting on npm since launch. What a minified bundle still gives away, and how to check your own site, is covered in [what users can see in your JavaScript and how to protect it](https://www.afterpack.dev/blog/protect-javascript-source-code?utm_source=devto&utm_medium=social&utm_campaign=launch).

In May 2026, I gave two popular obfuscators' own flagship demos to Claude Code with one four-paragraph prompt. [Claude Opus 4.6 came back with clean source](https://www.afterpack.dev/blog/ai-deobfuscates-javascript?utm_source=devto&utm_medium=social&utm_campaign=launch) in 10 minutes, Claude Opus 4.7 in 20. They were small demos, not whole apps, and those models are already a generation behind today's.

Your pricing rules, license checks, anti-fraud heuristics and unreleased features have always been in the bundle. What changed is who can read them, and how fast. What you still control is how much of that work carries over to your next release.

Yes. Given enough time, any obfuscator's output can be reversed, AfterPack's included. What AfterPack changes is carry-over: how much of the work to reverse one release still applies to the next. In our own measurement (September 2026, six seeds × five samples), under 6% of what a deobfuscator recovered from one build still resolved on the next ([how we measured](https://www.afterpack.dev/docs/comparison?utm_source=devto&utm_medium=social&utm_campaign=launch#how-we-measured)).

Logic someone has already read also stays read: if they worked out your discount rule once, a new build won't make them forget it.

What can expire is the tool built on that read. A script that strips a license check, a patcher for a paywall, an extractor that pulls your scoring rules out of every release: each is written against the structure of one build. The most popular open-source obfuscator emits fixed output shapes, and free public deobfuscators ship hardcoded recognizers for them, so a tool written once keeps working on every future build.

AfterPack starts from a new random seed on every build. Identifier names, encoded strings, the decoder's signature, state numbering and masked constants all come out different, so the details a tool hard-codes change every time. Run the engine inside a Worker and the window shrinks from a release to a single request.

A CLI, plugins for the bundlers you already use, a WebAssembly build for Workers, Pro cloud builds, a report of what each build protected, and a free site scanner.

| Fact | AfterPack | 
|---|---|
| What it is | A JavaScript obfuscator for production builds: it rewrites what your bundler produces, not your source files | 
| Engine | Rust, run locally by the CLI and the plugins, or as WebAssembly ( [`@afterpack/wasm`](https://www.afterpack.dev/docs/workers?utm_source=devto&utm_medium=social&utm_campaign=launch) ) inside your own Worker | 
| Output | Different on every build by default in the CLI and the plugins; pin a [seed](https://www.afterpack.dev/docs/builds?utm_source=devto&utm_medium=social&utm_campaign=launch) only when you need identical bytes | 
| License | CLI and plugins Apache-2.0; the engine is free to use under the AfterPack Engine License | 
| Price | Local engine free; Pro from $49 a month ( [plans](https://www.afterpack.dev/docs/tiers?utm_source=devto&utm_medium=social&utm_campaign=launch) ) | 
| Start | `npx afterpack@latest` after your build, or a[framework plugin](https://www.afterpack.dev/docs/frameworks?utm_source=devto&utm_medium=social&utm_campaign=launch) | 

What the table doesn't show:

`npx afterpack audit <url>`.
Identifiers vanish and string literals are encoded behind a runtime decoder; how much more happens depends on the preset. [`light`](https://www.afterpack.dev/docs/presets?utm_source=devto&utm_medium=social&utm_campaign=launch) (the default) encodes strings and rewrites syntax, adding no structural layers — the right baseline for most projects. `medium` makes production code meaningfully harder to follow; `hard` is for code that matters, like pricing, gating and license checks; `extreme` sets the highest complexity of the presets, best aimed at one function or file rather than a whole bundle.

A 139-byte function, before:

```
export function discount(plan, seats) {
  if (plan === "team" && seats >= 10) return 0.2;
  if (plan === "team") return 0.1;
  return 0;
}
```

And after one `npx afterpack@latest` run at the default `light` preset (27 September 2026):

```
export function discount(N, O) {
  if (N === Jr("T|P4") && O >= 10) return 0.2;
  if (N === Jr("T|P4")) return 0.1;
  return 0;
}
```

`"team"` is gone, replaced by a ciphertext and a decoder call; run it again and the names, the ciphertext and the decoder's signature all come out different. At `light`, numbers such as `10`, `0.2` and `0.1` stay readable; `medium` and up also mask integer constants like that `10`. There's overhead for the decoder that ships alongside the encoded strings, so a file this small comes out proportionally larger than a real bundle would: on a real bundle, `light` runs 1.9 to 2.4 times the input, gzipped ([presets](https://www.afterpack.dev/docs/presets?utm_source=devto&utm_medium=social&utm_campaign=launch)).

Paste your own function into the [playground](https://www.afterpack.dev/playground?utm_source=devto&utm_medium=social&utm_campaign=launch) to see what each preset does to it — no install, no account.

`Add AfterPack to this project: read afterpack.dev/llms.txt, follow the quickstart for my framework, then run a production build and show me the Protection Map.`
`npx afterpack@latest` after your next build.
[`@afterpack/wasm`](https://www.afterpack.dev/docs/workers?utm_source=devto&utm_medium=social&utm_campaign=launch) runs the engine as WebAssembly inside your own Worker, so a fresh seed on every call obfuscates each response differently:

``` js
import { obfuscate } from "@afterpack/wasm";

export default {
  async fetch(request) {
    const upstream = await fetch("https://example.com/app.js"); // your origin's bundle
    if (!upstream.ok) return upstream;
    const source = await upstream.text();
    const result = await obfuscate(
      { path: "app.js", source },
      { preset: "hard", seed: crypto.randomUUID() },
    );
    return new Response(result.bytes, {
      headers: { "content-type": "application/javascript" },
    });
  },
};
```

If we run that handler in Node on an Apple M2 Max (27 September 2026), serving the `discount` file from a local origin, four requests come back as four different outputs, each returning the right discounts. Once the module is warm, each obfuscation takes under 4 ms; the first call, which instantiates the engine, takes about 40 ms. That's a 139-byte input; a real bundle costs far more CPU, and it's paid on every response. The [Workers guide](https://www.afterpack.dev/docs/workers?utm_source=devto&utm_medium=social&utm_campaign=launch) covers the plan limits and when caching one shape per rotation window is the better trade.

The full setup, with wrangler config, one cached shape per time window and real `curl` output, is in the tutorial on [how to obfuscate JavaScript on every request with Cloudflare Workers](https://www.afterpack.dev/blog/obfuscate-javascript-cloudflare-workers?utm_source=devto&utm_medium=social&utm_campaign=launch).

Run the CLI after your build, or install the plugin for your bundler so every production build comes out obfuscated. The CLI needs no setup; run this in the project root after building:

```
npx afterpack@latest
```

It finds your build output (`dist/`, `.next/`, `.output/`, `build/` or `out/`), rewrites it in place and keeps a backup; `npx afterpack@latest restore` undoes the run.

To make obfuscation part of the build itself, install the plugin for your bundler:

```
npm install -D @afterpack/next   # or @afterpack/vite, @afterpack/webpack
```

Each plugin takes one small config change, such as wrapping your Next.js config in `withAfterpack(config)`; [frameworks](https://www.afterpack.dev/docs/frameworks?utm_source=devto&utm_medium=social&utm_campaign=launch) shows it for every supported bundler, from Astro to Parcel. To see what your live site exposes today, run it through the [scanner](https://www.afterpack.dev/security-scanner?utm_source=devto&utm_medium=social&utm_campaign=launch) first.

AfterPack raises the cost of reading your code and of reusing tooling against it; it cannot hide values from someone who instruments the running program, so secrets and anything that grants access belong on your server. What it does give you comes with the build you already run: a new target every release, with no extra step to remember.

Next is network obfuscation: [network transport cloaking](https://www.afterpack.dev/roadmap?utm_source=devto&utm_medium=social&utm_campaign=launch) will run your API requests and responses through a codec that changes with every build. Cross-module obfuscation will tie logic together across module boundaries, and [AfterPack on Workers for Platforms](https://www.afterpack.dev/roadmap?utm_source=devto&utm_medium=social&utm_campaign=launch) will serve a new shape per request with nothing to install. Past JavaScript, we're heading for HTML and CSS.

Yes. The CLI, the framework plugins and the local engine are free, and your source never leaves your machine. [Pro](https://www.afterpack.dev/docs/pro?utm_source=devto&utm_medium=social&utm_campaign=launch) adds cloud builds and per-region [directives](https://www.afterpack.dev/docs/directives?utm_source=devto&utm_medium=social&utm_campaign=launch) from $49 a month, and a registered Free workspace gets 10 MB of Pro builds a month. The full table is on [plans and tiers](https://www.afterpack.dev/docs/tiers?utm_source=devto&utm_medium=social&utm_campaign=launch).

The CLI and the framework plugins are, under Apache-2.0, on [GitHub](https://github.com/afterpack-dev/afterpack). The engine they run is free to use under the AfterPack Engine License.

Modestly, and more at stronger presets. The default is `light`. Raise the [preset](https://www.afterpack.dev/docs/presets?utm_source=devto&utm_medium=social&utm_campaign=launch) for a whole project, or use a Pro directive to aim `hard` or `extreme` at only the functions that need it.

It works on your production build through a plugin for the bundler you already use, and by default every build produces different output, so a deobfuscator written for one release mostly stops working on the next. Build time, output size and carry-over, measured side by side with other obfuscators, are on the [comparison page](https://www.afterpack.dev/docs/comparison?utm_source=devto&utm_medium=social&utm_campaign=launch).

No. Anything your running code uses can be observed by someone who runs it in a browser they control, so keep API keys and other secrets on your server. Encoding does keep a key out of a `grep` and out of automated secret scanners, which buys time to rotate one that shipped by accident ([best practices](https://www.afterpack.dev/docs/best-practices?utm_source=devto&utm_medium=social&utm_campaign=launch#if-you-shipped-a-secret-by-accident)).

Found a bug, or want a bundler we don't cover yet? Open an issue on [GitHub](https://github.com/afterpack-dev/afterpack/issues) with your bundler and its version; that's where I read feedback.

The code you ship has always been readable. From today, what someone learns from one release no longer has to carry over to the next.

— [Nikita](https://nikitaeverywhere.com)
