Skip to content

j9t/html-minifier-next

 
 

Repository files navigation

HTML Minifier Next

npm version Build Status

HTML Minifier is a highly configurable, well-tested, JavaScript-based HTML minifier.

The project has been based on Terser’s html-minifier-terser, which in turn had been based on Juriy Zaytsev’s html-minifier. It was set up because as of 2025, both html-minifier-terser and html-minifier have been unmaintained for some time. As the project seems maintainable [to me, Jens]—even more so with community support—, it will be updated and documented further in this place.

Installation

From npm for use as a command line app:

npm i -g html-minifier-next

From npm for programmatic use:

npm i html-minifier-next

Usage

Note that almost all options are disabled by default. Experiment and find what works best for you and your project.

Sample command line:

html-minifier-next --collapse-whitespace --remove-comments --minify-js true --input-dir=. --output-dir=example

Process specific file extensions:

# Process only HTML files (CLI method)
html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist --file-ext=html

# Process multiple file extensions (CLI method)
html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist --file-ext=html,htm,php

# Using configuration file with fileExt setting
html-minifier-next --config-file=html-minifier.json --input-dir=src --output-dir=dist

# Process all files (default behavior)
html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist

CLI Options

Use html-minifier-next --help to check all available options:

Option Description Example
--input-dir <dir> Specify an input directory --input-dir=src
--output-dir <dir> Specify an output directory --output-dir=dist
--file-ext <extensions> Specify file extension(s) to process (overrides config file setting) --file-ext=html or --file-ext=html,htm,php
-o --output <file> Specify output file (single file mode) -o minified.html
-c --config-file <file> Use a configuration file --config-file=html-minifier.json

Configuration Files

You can also use a configuration file to specify options:

JSON configuration example:

{
  "collapseWhitespace": true,
  "removeComments": true,
  "fileExt": "html,htm"
}

Using a configuration file:

# Specify config file
html-minifier-next --config-file=html-minifier.json --input-dir=src --output-dir=dist

# CLI arguments override config file settings
html-minifier-next --config-file=html-minifier.json --file-ext=xml --input-dir=src --output-dir=dist

Node.js

const { minify } = require('html-minifier-next');

const result = await minify('<p title="blah" id="moo">foo</p>', {
  removeAttributeQuotes: true,
});
result; // “<p title=blah id=moo>foo</p>”

See the original blog post for details of how it works, description of each option, testing results, and conclusions.

For lint-like capabilities take a look at HTMLLint.

Minification comparison

How does HTML Minifier compare to other solutions, like minimize or htmlcompressor.com?

Site Original size (KB) HTML Minifier minimize htmlcompressor.com
A List Apart 64 54 59 57
Amazon 206 195 203 200
BBC 767 703 761 n/a
CSS-Tricks 166 124 152 148
ECMAScript 7204 6361 6581 n/a
EFF 57 48 52 52
FAZ 1767 1641 1679 n/a
Frontend Dogma 119 114 128 118
Google 51 46 50 50
HTML Minifier 373 250 349 n/a
Mastodon 37 28 36 36
NBC 601 549 593 n/a
New York Times 822 701 811 n/a
United Nations 9 7 8 8
W3C 50 36 41 39
Wikipedia 225 204 215 215

Options quick reference

Most of the options are disabled by default.

Option Description Default
caseSensitive Treat attributes in case-sensitive manner (useful for custom HTML elements) false
collapseBooleanAttributes Omit attribute values from boolean attributes false
customFragmentQuantifierLimit Set maximum quantifier limit for custom fragments to prevent ReDoS attacks 200
collapseInlineTagWhitespace Don’t leave any spaces between display:inline; elements when collapsing. Must be used in conjunction with collapseWhitespace=true false
collapseWhitespace Collapse white space that contributes to text nodes in a document tree false
conservativeCollapse Always collapse to 1 space (never remove it entirely). Must be used in conjunction with collapseWhitespace=true false
continueOnParseError Handle parse errors instead of aborting. false
customAttrAssign Arrays of regex’es that allow to support custom attribute assign expressions (e.g. '<div flex?="{{mode != cover}}"></div>') []
customAttrCollapse Regex that specifies custom attribute to strip newlines from (e.g. /ng-class/)
customAttrSurround Arrays of regexes that allow to support custom attribute surround expressions (e.g. <input {{#if value}}checked="checked"{{/if}}>) []
customEventAttributes Arrays of regexes that allow to support custom event attributes for minifyJS (e.g. ng-click) [ /^on[a-z]{3,}$/ ]
decodeEntities Use direct Unicode characters whenever possible false
fileExt File extensions to process [] (process all files)
html5 Parse input according to HTML5 specifications true
ignoreCustomComments Array of regexes that allow to ignore certain comments, when matched [ /^!/, /^\s*#/ ]
ignoreCustomFragments Array of regexes that allow to ignore certain fragments, when matched (e.g. <?php ... ?>, {{ ... }}, etc.) [ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]
includeAutoGeneratedTags Insert elements generated by HTML parser true
inlineCustomElements Array of names of custom elements which are inline []
keepClosingSlash Keep the trailing slash on singleton elements false
maxInputLength Maximum input length to prevent ReDoS attacks (disabled by default) undefined
maxLineLength Specify a maximum line length. Compressed output will be split by newlines at valid HTML split-points
minifyCSS Minify CSS in style elements and style attributes (uses clean-css) false (could be true, Object, Function(text, type))
minifyJS Minify JavaScript in script elements and event attributes (uses Terser) false (could be true, Object, Function(text, inline))
minifyURLs Minify URLs in various attributes (uses relateurl) false (could be String, Object, Function(text))
noNewlinesBeforeTagClose Never add a newline before a tag that closes an element false
preserveLineBreaks Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a line break. Must be used in conjunction with collapseWhitespace=true false
preventAttributesEscaping Prevents the escaping of the values of attributes false
processConditionalComments Process contents of conditional comments through minifier false
processScripts Array of strings corresponding to types of script elements to process through minifier (e.g. text/ng-template, text/x-handlebars-template, etc.) []
quoteCharacter Type of quote to use for attribute values (“'” or “"”)
removeAttributeQuotes Remove quotes around attributes when possible false
removeComments Strip HTML comments false
removeEmptyAttributes Remove all attributes with whitespace-only values false (could be true, Function(attrName, tag))
removeEmptyElements Remove all elements with empty contents false
removeOptionalTags Remove optional tags false
removeRedundantAttributes Remove attributes when value matches default. false
removeScriptTypeAttributes Remove type="text/javascript" from script elements. Other type attribute values are left intact false
removeStyleLinkTypeAttributes Remove type="text/css" from style and link elements. Other type attribute values are left intact false
removeTagWhitespace Remove space between attributes whenever possible. Note that this will result in invalid HTML! false
sortAttributes Sort attributes by frequency false
sortClassName Sort style classes by frequency false
trimCustomFragments Trim white space around ignoreCustomFragments. false
useShortDoctype Replaces the doctype with the short (HTML5) doctype false

Sorting attributes / style classes

Minifier options like sortAttributes and sortClassName won’t impact the plain-text size of the output. However, they form long repetitive chains of characters that should improve compression ratio of gzip used in HTTP compression.

Special cases

Ignoring chunks of markup

If you have chunks of markup you would like preserved, you can wrap them <!-- htmlmin:ignore -->.

Minifying JSON-LD

You can minify script elements with JSON-LD by setting the option { processScripts: ['application/ld+json'] }. Note that this minification is very rudimentary, it is mainly useful for removing newlines and excessive whitespace.

Preserving SVG elements

SVG elements are automatically recognized, and when they are minified, both case-sensitivity and closing-slashes are preserved, regardless of the minification settings used for the rest of the file.

Working with invalid markup

HTML Minifier can’t work with invalid or partial chunks of markup. This is because it parses markup into a tree structure, then modifies it (removing anything that was specified for removal, ignoring anything that was specified to be ignored, etc.), then it creates a markup out of that tree and returns it.

Input markup (e.g. <p id="">foo) → Internal representation of markup in a form of tree (e.g. { tag: "p", attr: "id", children: ["foo"] }) → Transformation of internal representation (e.g. removal of id attribute) → Output of resulting markup (e.g. <p>foo</p>)

HTML Minifier can’t know that original markup was only half of the tree; it does its best to try to parse it as a full tree and it loses information about tree being malformed or partial in the beginning. As a result, it can’t create a partial/malformed tree at the time of the output.

Security

ReDoS protection

This minifier includes protection against regular expression denial of service (ReDoS) attacks:

  • Custom fragment quantifier limits: The customFragmentQuantifierLimit option (default: 200) prevents exponential backtracking by replacing unlimited quantifiers (*, +) with bounded ones in regular expressions.

  • Input length limits: The maxInputLength option allows you to set a maximum input size to prevent processing of excessively large inputs that could cause performance issues.

  • Enhanced pattern detection: The minifier detects and warns about various ReDoS-prone patterns including nested quantifiers, alternation with quantifiers, and multiple unlimited quantifiers.

Important: When using custom ignoreCustomFragments, ensure your regular expressions don’t contain unlimited quantifiers (*, +) without bounds, as these can lead to ReDoS vulnerabilities.

(Further improvements are needed. Contributions welcome.)

Custom fragment examples

Safe patterns (recommended):

ignoreCustomFragments: [
  /<%[\s\S]{0,1000}?%>/,         // JSP/ASP with explicit bounds
  /<\?php[\s\S]{0,5000}?\?>/,    // PHP with bounds
  /\{\{[^}]{0,500}\}\}/          // Handlebars without nested braces
]

Potentially unsafe patterns (will trigger warnings):

ignoreCustomFragments: [
  /<%[\s\S]*?%>/,                // Unlimited quantifiers
  /<!--[\s\S]*?-->/,             // Could cause issues with very long comments
  /\{\{.*?\}\}/,                 // Nested unlimited quantifiers
  /(script|style)[\s\S]*?/       // Multiple unlimited quantifiers
]

Template engine configurations:

// Handlebars/Mustache
ignoreCustomFragments: [/\{\{[\s\S]{0,1000}?\}\}/]

// Liquid (Jekyll)
ignoreCustomFragments: [/\{%[\s\S]{0,500}?%\}/, /\{\{[\s\S]{0,500}?\}\}/]

// Angular
ignoreCustomFragments: [/\{\{[\s\S]{0,500}?\}\}/]

// Vue.js
ignoreCustomFragments: [/\{\{[\s\S]{0,500}?\}\}/]

Important: When using custom ignoreCustomFragments, the minifier automatically applies bounded quantifiers to prevent ReDoS attacks, but you can also write safer patterns yourself using explicit bounds.

Running benchmarks

Benchmarks for minified HTML:

cd benchmarks
npm install
npm run benchmark

Running local server

npm run serve

Acknowledgements

With many thanks to all the previous authors of HTML Minifier, especially Juriy Zaytsev, and to everyone who helped make this new edition better, like Daniel Ruf.

About

Maintained fork of html-minifier-terser, which is a fork of html-minifier

Resources

License

Stars

Watchers

Forks

Sponsor this project

Languages

  • JavaScript 100.0%