Gulp Plugin

Obfuscate JavaScript in a Gulp task

Plenty of sites still ship plain script files through a Gulp task instead of a bundler. One .pipe() protects every JavaScript file in the stream, lets CSS and vendor files pass through, and drops source maps on the way to dest(). The free options run on your own machine with no account.

Tested 2026-10-29

Real task, same behaviour

gulp 5.0 and javascriptobfuscator-com 0.5.0, no account. The protected script behaved exactly like the original; CSS and the excluded vendor file came out byte-for-byte unchanged.

Protects.js, .mjs and .cjs files
BatchesAll files in one run, at stream end
Source maps.js.map files dropped
Setup

One pipe between src() and dest()

The plugin ships inside javascriptobfuscator-com as javascriptobfuscator-com/gulp. This task copies a site folder to dist, protecting the scripts and leaving the vendor folder alone.

Terminal
npm install --save-dev gulp javascriptobfuscator-com
gulpfile.js — run with npx gulp site
const { src, dest } = require("gulp");
const jsoProtector = require("javascriptobfuscator-com/gulp");

exports.site = () =>
  src(["site/**/*.js", "site/**/*.css"], { base: "site" })
    .pipe(jsoProtector({ exclude: ["js/vendor/**"] }))
    .pipe(dest("dist"));

What the pipe does

  1. Collects every file in the stream and waits for the end of the stream, so all scripts are protected in one batch — one engine run, or one hosted request with a paid preset.
  2. Protects the files whose path ends in .js, .mjs or .cjs and matches no exclude pattern. Patterns are relative to the glob base, here js/vendor/**.
  3. Passes everything else through untouched: CSS, images, excluded scripts.
  4. Drops .js.map files, which would describe the unprotected code.
  5. Picks the engine. No API key and only free options: protected locally, nothing uploaded. With a key, paid options run on the hosted engine.
Real Output

A classic script, before and after

A cart script of the kind many Gulp-built sites carry: a global function, a storage key and a discount code. The seven-line comment that opens the free output is left out below.

site/js/cart.js
function addToCart(sku, qty) {
  var cart = JSON.parse(localStorage.getItem("cart") || "[]");
  var discountCode = "SPRING-24";
  cart.push({ sku: sku, qty: qty, code: discountCode });
  localStorage.setItem("cart", JSON.stringify(cart));
  return cart.length;
}
dist/js/cart.js
function addToCart(b,c){var a=JSON.parse(localStorage.getItem("\x63\x61\x72\x74")||"\x5b\x5d");var d="\x53\x50\x52\x49\x4e\x47\x2d\x32\x34";a.push({sku:b,qty:c,code:d});localStorage.setItem("\x63\x61\x72\x74",JSON.stringify(a));return a.length;}

Run against the same stubbed localStorage, both versions returned [1,2] and stored the same cart. Note what the free engine keeps: addToCart is a global that HTML onclick attributes may call, so its name stays; so do property names such as sku and qty. Local variables are renamed and quoted strings encoded. Hex escapes are a first layer — the paid presets add string encryption, flattening and runtime defences.

Watch Out For

Gulp-specific pitfalls

  • Keep buffered mode. With src(..., { buffer: false }) the plugin cannot read whole files; the task fails and Gulp only says it “did not complete”.
  • Put it after concatenation and minification. If the task also runs gulp-concat or a minifier, pipe the obfuscator last, just before dest(), so nothing re-formats the protected code.
  • Set base when globbing several folders, so exclude patterns and output paths are what you expect.
  • Globals used from HTML keep their names with the free engine. With Replace Globals on a paid preset, list them in reservedNames.
Options

Passed to jsoProtector({ ... })

  • preset — free by default with no API key; standard, balanced and maximum need a plan.
  • exclude / include — glob patterns relative to the glob base.
  • reservedNames — names the renamer must keep, such as ["addToCart"].
  • removeSourceMaps: false — let .js.map files through.
  • manifest — write a JSON record of what was protected.
  • Paid presets read JSO_API_KEY and JSO_API_PASSWORD from the environment.
Frequently Asked

Gulp obfuscation questions

Can I pipe CSS and images through the same task?

Yes. The plugin protects only files ending in .js, .mjs or .cjs and passes every other file through unchanged, so one task can copy a whole site folder.

Does it work with buffer: false?

No. The plugin needs whole files. With src(..., { buffer: false }) the task fails, and Gulp only reports that the task did not complete. Use the default buffered mode.

Is this the gulp-javascript-obfuscator package?

No. gulp-javascript-obfuscator wraps the open-source javascript-obfuscator package. This plugin is part of javascriptobfuscator-com, the npm package for this site’s engine. The CLI’s --migrate-javascript-obfuscator command converts a javascript-obfuscator options file into a jso.config.json — see migrating from obfuscator.io.

Do I need an account?

No. With no API key the plugin protects on your own machine with the free engine: local variables renamed and string literals encoded, nothing uploaded, no rate limit. The output starts with a short comment naming the product. Paid options run on the hosted engine and need a plan.

Other Build Tools

Not using Gulp?

The same package protects Rollup, esbuild, Rspack and Parcel builds, or any folder from the command line. Sites with scripts inside HTML or PHP pages can use the Windows desktop app, which protects them in place.