What minification saves, and what gzip already did

Real measurements of minified JavaScript and CSS before and after gzip, why the saving shrinks, what renaming adds, and how source maps fit in.

On this page

"Minify your JavaScript" is one of the first items on every performance checklist, usually with an impressive percentage next to it. The percentage is real but it is measured on the wrong number. Browsers rarely download raw files: servers send them compressed with gzip or Brotli, and compression already removes most of what minification removes. This article measures both on real files, explains why the gap exists, and covers the parts of minification that do pay off, plus the source maps that make minified code debuggable. The numbers come from the HTML, CSS and JS minifier and Node.js 22's zlib.

The measurements

Three real files, minified by removing whitespace and comments only (what the tool does), then compared with esbuild 0.28's full minification, which also renames variables and rewrites syntax:

FileRawWhitespace removedFull minify
React 19.2 react.development.js47,219 B32,393 B (−31%)20,727 B (−56%)
same, gzipped10,755 B9,174 B (−15%)7,340 B (−32%)
js-yaml 4.3 js-yaml.js131,628 B79,318 B (−40%)44,797 B (−66%)
same, gzipped22,424 B19,168 B (−15%)15,143 B (−32%)
sonner 2.0 styles.css17,681 B15,143 B (−14%)
same, gzipped3,297 B3,052 B (−7%)

Two patterns stand out. First, removing whitespace saves 30 to 40 percent of a hand-written JavaScript file, but only about 15 percent of what is actually transferred. Second, the CSS file was already fairly compact, so the saving is smaller everywhere.

Why gzip eats most of the saving

Gzip (the DEFLATE algorithm) does two things: it replaces repeated byte sequences with short back-references, and it encodes frequent symbols with fewer bits. Indentation is the most repetitive thing in a source file: a line starting with eight spaces costs a few bits once those eight spaces have appeared before. Comments are prose and compress well too. So deleting them removes bytes that gzip was already storing cheaply.

What gzip cannot remove is information. Long names such as formattedPrice are repeated, so they compress, but every occurrence still costs a back-reference. That is why the full minifier, which renames such a variable to a single letter, still saves another 17 percentage points after gzip: it removes real information, not just repetition.

When the remaining bytes matter

A 1.6 KB difference after gzip is invisible on a fast connection and small on a slow one. Minification matters more for:

  • Parse and compile time. The browser parses the decompressed file, so 32 KB parses faster than 47 KB. On low-end phones this is often a bigger cost than the download.
  • Inline code. A <script> or <style> inside HTML is part of the HTML response, and if the HTML is not cached it is downloaded on every page view.
  • Places without compression. Email HTML, embedded widgets, files stored in a database or sent through an API that does not compress.
  • Size limits. Some platforms cap a snippet, a bookmarklet or a custom-code field at a fixed number of characters.

What a safe minifier must not change

Removing whitespace sounds trivial, but JavaScript has traps:

  • a + +b (a plus the number b) must not become a++b, which is a syntax error, and a - -b must not become a--b.
  • return followed by a line break returns undefined; joining the lines would return the value on the next line. The same goes for a line break before ++, and after throw, break and continue.
  • x = a / b / c contains two divisions, while x = /b/g is a regular expression. A minifier that guesses wrong collapses spaces inside the regex.
  • Template literals keep their whitespace exactly: ` a b is a different string from a b `.
  • 1 .toString() needs its space, because 1. would be read as a number.

In CSS, calc(100% - 10px) needs the spaces around the minus sign, and a :hover (any hovered element inside an a) is different from a:hover. In HTML, the space between </b> <i> is visible text, while the space between </li> <li> is not.

The tool's tests check this the strict way: it minifies real libraries such as React's development build and verifies, with an independent JavaScript parser, that the result has exactly the same syntax tree as the original.

Source maps

A minified bundle is hard to debug: everything is on a few lines, and with renaming every name is a single letter. A source map is a separate JSON file that maps each position in the minified file back to a line and column in the original sources, and to the original names. The minified file points to it with a final comment:

//# sourceMappingURL=app.min.js.map

Browser developer tools download the map only when they are open, so it costs normal visitors nothing. Two practical points: the map usually contains your full source code, so decide whether you want it public, and any tool that rewrites the minified file afterwards (including this one, which drops comments) also drops or invalidates the mapping. Generate source maps in your build tool, not by hand.

Common mistakes

  • Comparing raw sizes. Always compare gzipped or Brotli sizes, which is what users download. The tool shows both.
  • Minifying already minified code. It saves almost nothing and makes the source map wrong.
  • Removing licence comments. Many open-source licences require the notice to stay with the code. Keep /*! … */ comments.
  • Expecting renaming from a whitespace minifier. The large wins come from renaming and dead-code removal, which need a bundler.
  • Forgetting compression on the server. Turning on gzip or Brotli saves far more than any minifier; check the Content-Encoding response header.

Try it

Paste a file into the HTML, CSS and JS minifier and compare the raw and gzipped figures. If the gzipped saving is a few percent, the file was already in good shape; if your server does not compress at all, fix that first.

Tools mentioned in this article

Back to the blog