Library / SDK
javascript-obfuscator/webpack-obfuscator avatar
javascript-obfuscator/webpack-obfuscator

webpack-obfuscator: hiding bundle source without leaving the build

javascript-obfuscator plugin for Webpack

930 stars92 forksTypeScriptBSD-2-Clause

At a glance

What is it?
webpack-obfuscator is a Webpack 5 plugin and loader that runs javascript-obfuscator over your output bundles or individual modules, with bundle name excludes and an optional path through the obfuscator.io Pro API. It suits teams shipping browser JavaScript who want obfuscation to be part of the build rather than a manual step.
Who is it for?
Take webpack-obfuscator when your team ships browser JavaScript, already builds with Webpack 5, and wants obfuscation applied inside the build with a small amount of configuration rather than a separate script run over finished files. Skip it if you are still on Webpack 4, since the README points you to version 2 of the plugin for that case, or if you need to protect secrets, because obfuscation changes how code reads, not what it protects.
Can I use it commercially?
Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A build step, not a separate tool run

webpack-obfuscator is a plugin and loader for Webpack 5 that wraps the javascript-obfuscator project. The description calls it a javascript-obfuscator plugin for Webpack, and the package.json version is 3.6.1 with a description of javascript-obfuscator plugin for Webpack@5.

The value of wrapping rather than reinventing is in the option handling. The plugin exposes a single `obfuscatorOptions` property of type `Object` with a default of `null`, and the documentation is explicit that these are options for javascript-obfuscator which should be passed exactly as described on that project's own page. There is no second option vocabulary to learn, and no possibility of the two drifting apart, because the plugin passes them through.

That pass-through design also sets the boundaries. Whatever javascript-obfuscator can do is what you get, and the plugin adds bundling concerns: which files to touch, in what order relative to other loaders, and how to exclude pieces. It is a small project by design, and its dependency list is correspondingly short, with `multi-stage-sourcemap` and `multimatch` as the two runtime dependencies.

The plugin form excludes bundles by output file name

The plugin variant takes an options object and an optional second argument, the excludes list, which is an `Array` or `String` with a default of `[]`. The documentation is specific about what the strings match: the bundle name is the output file name after webpack compilation, and with multiple webpack entries you set the bundle name in the `output` object using the `[name]` or `[id]` aliases.

That detail decides whether your excludes actually work. The pattern has to be written against the filename webpack emits, not against the source file you had in mind. The syntax follows the multimatch package, and the documented examples are `['excluded_bundle_name.js', '**_bundle_name.js']` or the plain string `'excluded_bundle_name.js'`.

The README notes that this can be used to bypass obfuscation of some files, which is the usual reason to exclude something: a vendor chunk, a polyfill, or a file where a stack trace has to stay readable. The full configuration example shows two entries and two outputs:

javascript
// webpack.config.js
'use strict';

const JavaScriptObfuscator = require('webpack-obfuscator');

module.exports = {
    entry: {
        'abc': './test/input/index.js',
        'cde': './test/input/index1.js'
    },
    output: {
        path: 'dist',
        filename: '[name].js' // output: abc.js, cde.js
    },
    plugins: [
        new JavaScriptObfuscator({
            rotateStringArray: true
        }, ['abc.js'])
    ]
};

With that filename pattern in place, the `abc.js` bundle is skipped and `cde.js` is obfuscated.

The loader form needs to run last in the chain

The loader variant works at module level rather than bundle level. The documentation says to define a rule in the webpack config and use the obfuscator loader as the last of the loaders for your modules, and to add the `enforce: 'post'` flag to ensure the loader is called after normal loaders.

That ordering rule is the whole mechanism. Loaders run in the opposite order to the way they are written, and a post loader is invoked after the normal ones, so the obfuscator sees the code as other loaders have left it rather than the original source. If a normal loader such as a transpiler runs afterwards, it will process already obfuscated output, which is exactly the situation the flag exists to prevent.

javascript
var WebpackObfuscator = require('webpack-obfuscator');

// webpack loader rules array
rules: [
    {
        test: /\.js$/,
        exclude: [
            path.resolve(__dirname, 'excluded_file_name.js')
        ],
        enforce: 'post',
        use: {
            loader: WebpackObfuscator.loader,
            options: {
                rotateStringArray: true
            }
        }
    }
]

Note that the excludes here are file paths matched through the rule's own `exclude` array, using `path.resolve`, not bundle names. That is the structural difference between the two modes: the plugin decides by output filename, the loader decides by source path.

Installation requires Webpack 5 and a peer dependency

The install step is a single dev dependency command, and the version constraint matters more here than in most packages:

bash
npm install --save-dev javascript-obfuscator webpack-obfuscator

The README states that the plugin requires Webpack@5, and that projects depending on Webpack@4 should use version 2 of the plugin instead. The package.json confirms this at the peer dependency level, with `webpack` at `^5.1.0` and `javascript-obfuscator` at `^4.0.0 || ^5.0.0`.

That peer range is worth noting because it is wider than what the Pro API path needs. The Pro API documentation states that the plugin and loader support the obfuscator.io Pro API for cloud based obfuscation and that this requires `javascript-obfuscator` `^5.0.0`. So a project on the older supported line can still use the local path, while the cloud path is gated on the newer major version.

The package is written in TypeScript, with `index.ts` at the root and separate `plugin/` and `loader/` directories. The build step is `npx tsc`, and the test scripts build first and then run jest, with separate `test:plugin` and `test:loader` configurations that invoke webpack against config files under `test/config/`.

The Pro API path sends code to obfuscator.io

The plugin can route obfuscation through the paid obfuscator.io Pro API instead of doing it locally, which the documentation says allows you to use Virtual Machine based obfuscation available through the JavaScript Obfuscator Pro API. Virtual Machine based obfuscation is not something the open source package provides, so this is a genuinely different output, not just the same result sent over the network.

The plugin form takes a third argument for the Pro API configuration and an optional fourth for progress:

javascript
var WebpackObfuscator = require('webpack-obfuscator');

plugins: [
    new WebpackObfuscator(
        {
            // obfuscator options
            rotateStringArray: true
        },
        ['excluded_bundle_name.js'],  // excludes
        {
            // Pro API configuration
            apiToken: 'your-api-token-from-obfuscator.io',
            timeout: 300000  // optional, request timeout in ms (default: 5 minutes)
        },
        (message) => {
            // optional progress callback
            console.log('Obfuscation progress:', message);
        }
    )
]

The configuration object has one required property, `apiToken`, and one optional property, `timeout`, a number defaulting to `300000` milliseconds. The loader form exposes the same thing as `proApiConfig` inside the loader options plus an `onProgress` callback, and the package exports TypeScript types named `IProApiConfig` and `TProApiProgressCallback` for it.

The trade-off is that with this path your source is uploaded to a third party service during the build. That is a decision about where your code may be transmitted, and it needs an answer before it is switched on, not after.

What the plugin does not promise

Three limits are visible in the documentation itself.

First, obfuscation is not encryption. The project hands your source to javascript-obfuscator, which restructures the code so it is harder to read and harder to trace back to your source. It does not turn a shipped bundle into something a reader cannot recover, and the README does not claim otherwise, so treating the output as a protection boundary for secrets inside the bundle is a misreading.

Second, the documentation says nothing about what obfuscation costs at build time. There is no stated figure for build duration, bundle size or debuggability trade-offs, and the plugin's own scripts show a build and test flow but no performance note. If build time is a constraint on your pipeline, measure it on your own code with `rotateStringArray: true` before committing to it.

Third, the excludes semantics are asymmetric between the two modes, as described above, and a pattern that does not match leaves you silently obfuscating a file you wanted to keep readable, or skipping one you wanted obfuscated. There is no dry run or report of which bundles were processed. The Pro API path adds a fourth limit, since a build now depends on a remote service and an API token being present.

BSD-2-Clause terms and a quiet patch line

The licence field on the repository says BSD-2-Clause, and the README reproduces the two clause BSD text with the copyright line naming Timofey Kachalov and the year 2025. The obligation is the standard one: source redistributions must retain the copyright notice and conditions, and binary redistributions must reproduce them in the documentation or other materials. It is a permissive licence with no copyleft requirement, so including the plugin in a commercial build does not oblige you to publish your own application code.

The maintenance picture deserves a careful read. The last push was on 2026-04-07. The listed GitHub releases are much older, with 3.5.0 on 2021-10-17, 3.4.1 on 2021-08-09 and 3.4.0 on 2021-07-20, while package.json already carries version 3.6.1. In other words, the published release tags stopped well before the current package version, and the recent push is a fix or change landed on the branch rather than a new tag.

For a build dependency that is usually tolerable, because the plugin mostly forwards options to javascript-obfuscator and the peer dependency range already accepts two major versions of it. But it does mean there is no tagged changelog to consult when you want to know what changed, so reading the diff is the only route to that answer.

Editorial conclusion

Take webpack-obfuscator when your team ships browser JavaScript, already builds with Webpack 5, and wants obfuscation applied inside the build with a small amount of configuration rather than a separate script run over finished files. Skip it if you are still on Webpack 4, since the README points you to version 2 of the plugin for that case, or if you need to protect secrets, because obfuscation changes how code reads, not what it protects. Verify first that the `excludes` patterns match your output file names under `output.filename`, and that `javascript-obfuscator` resolves to a version your peer dependency range accepts. The licence field on the repository says BSD-2-Clause, and the last push was on 2026-04-07.

Frequently asked questions

How do I install webpack-obfuscator?

The README gives one command, `npm install --save-dev javascript-obfuscator webpack-obfuscator`. The plugin requires Webpack 5, and projects on Webpack 4 are pointed to version 2 of the plugin.

How do I exclude a bundle from obfuscation in webpack-obfuscator?

Pass an array or string as the second argument to the plugin. The names are matched against the output file name after compilation, using multimatch syntax, for example `['excluded_bundle_name.js', '**_bundle_name.js']`. The loader mode instead excludes by source path in the rule's `exclude` array.

How do I use the obfuscator.io Pro API with webpack-obfuscator?

The plugin takes a Pro API configuration object with a required `apiToken` and an optional `timeout` in milliseconds, defaulting to 300000, and an optional progress callback. The loader form exposes the same settings as `proApiConfig` and `onProgress`. This path requires `javascript-obfuscator` `^5.0.0`.

Why should the obfuscator loader use enforce post?

The README instructs you to use the obfuscator loader as the last of the loaders for your modules and to add the `enforce: 'post'` flag so the loader is called after normal loaders. That ordering ensures the obfuscator sees the module after other loaders have processed it.

What licence is webpack-obfuscator released under?

The licence field on the repository says BSD-2-Clause. The README reproduces the two clause BSD licence text with the copyright line naming Timofey Kachalov and the year 2025.

Official sources

  1. javascript-obfuscator/webpack-obfuscator on GitHub
  2. License: BSD-2-Clause
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/javascript-obfuscator-webpack-obfuscator.svg)](https://hysenlabs.com/projects/javascript-obfuscator-webpack-obfuscator)

Was this useful?