Svelte & SvelteKit

Svelte compiles your components away. It doesn’t hide them.

The compiler’s whole pitch is that there is no framework runtime shipping to the browser — just your logic, as small imperative JavaScript. Which means the file your visitors download is your application, with nothing standing between a reader and your stores, load functions, and pricing rules. Obfuscate the built client bundle so that file stops explaining how your product works.

Reality Check

vite build → .svelte-kit/output/client/

Small, fast, and completely legible after one formatter pass. Compiled is not the same as protected.

Server code stays server-side+page.server.js and $lib/server never reach the client — use that.
Protect the output.svelte files are compiler input; the emitted chunks are the release.
Contracts preservedRoute paths, custom element names, and persisted keys stay reserved.
Threat Model

The client bundle is the whole client program

Svelte’s efficiency is a security fact as much as a performance one: with no framework runtime in the way, almost everything in the downloaded chunk is yours. Your derived stores, your universal load functions, your feature gates, your API shapes. None of that is hidden by compilation or minification. The only variable is how long it takes a stranger to read it.

Stores describe your domain

Writable and derived stores in clear text are a free schema of your product — entitlements, limits, workflow states, and the names you chose for all of them.

Universal load is public

A +page.js load function runs on both sides, which means it ships. Anything it does — endpoints, query shapes, fallback logic — is readable by anyone who opens the chunk.

Gates read as instructions

A visible if (user.plan === 'pro') tells an attacker exactly which value to fake and which branch to flip. Protected control flow makes that a project rather than a glance.

Protect after the Svelte compiler runs
npm run build                 # vite + svelte -> build output
npx jso-protector \
  --preset maximum \
  --input build \
  --output build-protected
# deploy build-protected/ instead of build/
The Order of Operations

Compile → protect → deploy

  • Never obfuscate .svelte files. The compiler must parse your markup, script, and style blocks as written.
  • Protect the client output in one pass so identifiers agree across the entry chunk and every code-split route chunk.
  • Point the tool at the adapter’s output folder — whatever your adapter writes, that is the artifact your users receive.
  • Reserve the runtime string contracts — route paths, custom element tag and attribute names, persisted store keys, and any key read off an API response.
  • Smoke test the protected build — sign in, hit a guarded route, submit a form, navigate to a code-split route, refresh a page that rehydrates persisted state.
The SvelteKit Advantage

Use the server/client split you already have

SvelteKit gives you something most SPA stacks do not: a compiler-enforced boundary. Modules ending .server.js and anything under $lib/server are excluded from the client build by design — importing them from client code is an error, not a subtle leak. That boundary is free protection, and it is worth spending before you spend on transforms.

Move the decision, not just the code

Entitlement checks, pricing maths, and anything with a secret belong in +page.server.js or a form action. What ships to the browser should render the answer, not compute it.

Then protect what must ship

Plenty still has to run client-side — interactive logic, offline behavior, proprietary algorithms. That remainder is what obfuscation is for.

Watch what load returns

Data returned from a server load is serialized into the page for hydration. Anything you put there is public, however protected the code that reads it.

What To Exclude

Names Svelte matches as strings at runtime

Renaming is safe where the compiler already resolved the reference. It is unsafe where a literal string is matched against a name while the app runs. That line is the entire configuration.

Route paths and params

SvelteKit builds routes from the filesystem, and the generated manifest matches path strings against the URL. Those literals, and the param names your code reads off them, are contracts with the browser.

Custom element contracts

If you compile components to custom elements, the tag name and observed attribute names are matched as strings by the host page. Reserve them like public SDK methods.

Persisted store keys

Anything written to localStorage in one release and read in the next is a storage contract. Rename it and rehydration quietly fails for every returning user.

Layered Defense

Three layers doing three different jobs

Minification — the size layer

Vite already does it. Smaller download, fully reversible with a formatter. Useful, and not a security control — see minification vs obfuscation.

Obfuscation — the comprehension layer

Encrypted string pool, flattened control flow, a per-build polymorphic decoder, and VM bytecode for the functions that matter most. This is what survives beautifying.

Runtime defense — the behavior layer

Runtime defense spots tampering and debugger attachment, so a modified bundle reports in instead of quietly hammering your API.

Frequently Asked

Svelte protection questions, answered

Isn’t compiled code already hard to read?

No. Compiled Svelte output is unusually clean imperative JavaScript. Your names, strings, and logic order survive the compiler intact.

Do I protect src or the build folder?

The build folder. The compiler needs your source as written; protection consumes the JavaScript it emits.

Does this work with any adapter?

Yes — static, Node, or a platform adapter. Protect whatever client assets the adapter produces, and deploy the protected copy.

What about server routes?

They never reach the browser, so they are not the obfuscation problem. Move as much authority there as you can; protect the remainder.

Will it hurt performance?

Renaming and string protection are effectively free. VM protection is meaningfully slower than native JavaScript, so scope it to licensing and entitlement paths — never to a reactive update path.

How do I read production stack traces?

Keep each build’s identifier map private and use symbolication to translate traces back to real names.

Start Now

Read your own bundle the way an attacker would

Open your deployed Svelte app, grab the main client chunk from the network panel, and run it through a formatter. Everything you can read, so can everyone else. Then paste that same file into the online obfuscator and compare.

Related Guides

Protecting other JavaScript targets

Same protection engine, different build pipeline. These guides cover the platforms closest to this one:

Vue apps · Angular apps · Next.js apps · TypeScript projects · Protecting JavaScript (overview)