This page is available in English only.

Obfuscate an Astro site

@afterpack/astro is a thin integration that wires @afterpack/vite into Astro's own Vite pipeline.

Astro compiles every island and client bundle through its own Vite pipeline, and AfterPack integrates at the bundler level. @afterpack/astro exists so you don't have to hand-wire vite: { plugins: [...] } yourself.

Everything happens in @afterpack/vite's generateBundle, inside Astro's own Vite pipeline.

Install

$ npm install -D @afterpack/astro
// astro.config.mjs
import { defineConfig } from "astro/config";
import afterpack from "@afterpack/astro";

export default defineConfig({
  integrations: [afterpack()],
});

The default export and the named afterpackAstro export are the same function.

Build

astro build now emits obfuscated output. astro dev is untouched, because the plugin only runs on the build's generateBundle, the hook @afterpack/vite always uses. Astro writes the obfuscated chunks; the originals never reach dist/.

The obfuscated surface is the JavaScript Astro emits: island bundles and client scripts, typically under dist/_astro/. Server-rendered .astro template logic that never becomes client JS is out of scope.

Verify

An astro build runs with the production build mode, so ask for the Protection Map explicitly when you want to inspect one:

integrations: [afterpack({ protectionMap: { enabled: true } })],

It writes 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 here as the options object, forwarded verbatim to @afterpack/vite, for example afterpack({ preset: "hard", complexity: 40 }). The type alias AfterpackAstroOptions is AfterpackViteOptions:

integrations: [afterpack({ seed: "git", preset: "medium" })],

Directives

Directives are read from your source by the plugin, before minification. Block comments only:

Directives inside .astro component files themselves are not captured. Put the directive in the island's .jsx/.tsx file:

/* @afterpack preset=hard */
export function LicenseGate({ token }) {
  // ...
}
/* @afterpack end */

Next