Obfuscate a Nuxt app

@afterpack/nuxt is a Nuxt 3 module that registers @afterpack/vite on Nuxt's Vite config.

Nuxt 3 bundles with Vite, and AfterPack integrates at the bundler level, so @afterpack/nuxt is a thin Nuxt module: it calls addVitePlugin to register @afterpack/vite on Nuxt's Vite config, and the client bundle is obfuscated in Vite's generateBundle, before the build is written.

Nuxt 2 projects build with webpack. Use @afterpack/webpack there instead.

Install

$ npm install -D @afterpack/nuxt
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ["@afterpack/nuxt"],
  afterpack: {
    seed: "git",
  },
});

Options live under the afterpack config key.

Build

nuxt generate (static) or nuxt build (SSR) now emits obfuscated output. nuxt dev is untouched, because the plugin only runs on Vite's generateBundle. A fresh @afterpack/vite plugin instance is registered per Vite environment (client and server), and each one obfuscates its own bundle before Vite writes it.

Static vs. server

  • Static / prerendered (nuxt generate): the prerendered client bundle in .output/public/_nuxt/ is obfuscated. This is the recommended shape for shipping protected client code.
  • Server (SSR): the client bundle is obfuscated the same way; the Nitro server bundle is your own deployment artifact.

Pin seed: "git" when you want the client legs of one release built from the same seed, or when a build has to be byte-reproducible (cache hits, golden snapshots). Builds & CI covers the trade. Nuxt also generates a fresh buildId per build.

Verify

export default defineNuxtConfig({
  modules: ["@afterpack/nuxt"],
  afterpack: { protectionMap: { enabled: true } },
});

Writes the combined Protection Map to the gitignored .afterpack/protectionMap.html. The source-map side effect and the handling rules are @afterpack/vite's.

entitlements.jsmedium · complexity 8Open full size
entitlements.js, 54 lines, built at the medium preset with a directive around the signature check. Click any token to see what was applied to it.

Options

Every configuration key can be passed under the afterpack config key, forwarded verbatim to @afterpack/vite, for example afterpack: { preset: "hard", complexity: 40 }. The type alias AfterpackNuxtOptions is AfterpackViteOptions.

Directives

A /* @afterpack … */ marker inside a .vue file is not captured. Put the directive in a plain .ts/.js module (a composable, a utils/ file) that the component imports instead.

Next