HTML Minifier
Collapse the whitespace a browser ignores while the whitespace it renders survives — pre, textarea and inline-tag gaps intact, every removal listed and measured.
<pre> and <textarea> bodies come back byte-identical — no option here can touch them; <script> and <style> bodies too, unless you tick a minify-embedded box below.
- Collapses runs of spaces, tabs and newlines outside <pre>, <textarea>, <script> and <style> — one space always survives between inline tags.
- Also deletes whitespace that touches a tag — the gap in a <b>b</b> c is content a browser renders, and it goes.
- Deletes <!-- … --> — conditional comments (<!--[if) and SSI directives (<!--#) are kept.
- Drops </li>, </td> and the rest where HTML permits omission — anything downstream that parses with a stricter parser will disagree.
- Unquotes values with no whitespace, quote, =, <, > or /, and never the last value before /> — a later hand edit that adds a space then breaks the tag.
- Rewrites disabled="disabled" to disabled for the documented boolean set only — hidden="until-found" is left alone.
- Drops empty class, style and on* attributes only — alt="" is meaningful and is never removed.
- Drops type="text/javascript" on <script> and type="text/css" on <style> and <link> — both are the HTML defaults.
- Runs <style> bodies through this site's CSS minifier — a body it refuses is kept verbatim and reported.
- Runs <script> bodies through this site's JavaScript minifier, loaded on first use — a body it refuses is kept verbatim and reported.
Press ? for shortcuts
Do it offline:$
How to use it
- Paste markup or drop an .html file — whitespace collapsing and comment removal start on, nothing else does.
- Turn the extra switches on one at a time; three of them are marked “may change rendering”.
- Read the savings row: original bytes, minified bytes, percentage saved and gzipped weight.
- Switch the output pane to Diff to see each removal in place, then copy, download or share the state.
HTML has two kinds of whitespace: the indentation a browser throws away, and the single space between words that it draws on screen. This page removes the first kind and keeps the second, so a preformatted block or a text area comes back with every space it started with. Savings are measured in bytes, and the markup never leaves this device.
Examples
<!DOCTYPE html>
<html lang="en">
<head>
<!-- build stamp 2026-08-12 -->
<meta charset="utf-8">
<title>Pricing</title>
</head>
<body>
<h1>Pricing</h1>
<p>Plans start at <strong>$9</strong>.</p>
</body>
</html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Pricing</title> </head> <body> <h1>Pricing</h1> <p>Plans start at <strong>$9</strong>.</p> </body> </html> Defaults only — collapse whitespace, remove comments. Savings row: 237 B → 177 B · saved 25.3%, with the readouts “Whitespace collapsed in 12 text runs.” and “1 comment removed.” Each indented break becomes one space, not nothing.
<div class="demo">
<pre> npm run build
--watch </pre>
<textarea name="notes"> keep
my spacing </textarea>
</div><div class="demo"> <pre> npm run build
--watch </pre> <textarea name="notes"> keep
my spacing </textarea> </div>Only the three gaps between the outer tags move — 130 B → 126 B · saved 3.1%. Both bodies keep their double spaces and their line break, and the run says so: “2 whitespace-sensitive elements (pre, textarea, listing, plaintext) returned byte-identical.”
<p>Read the
<a href="/docs">docs</a>
before you <b>upgrade</b>.</p><p>Read the <a href="/docs">docs</a> before you <b>upgrade</b>.</p>Two line breaks around a link collapse to one space each — 71 B → 67 B · saved 5.6%. Deleting them instead would render “thedocs” as one word.
<form action="/subscribe" method="post">
<input type="email" name="email" class="" required="required">
<input type="checkbox" checked="checked" disabled="disabled">
<script type="text/javascript" src="/a.js"></script>
</form><form action="/subscribe" method="post"> <input type="email" name="email" required> <input type="checkbox" checked disabled> <script src="/a.js"></script> </form>Three safe attribute switches: 232 B → 162 B · saved 30.2%, reported as “3 boolean attributes collapsed to the name alone.”, “1 empty attribute removed (class, style and on* only).” and “1 redundant type attribute removed.” An empty alt stays.
<ul><li>One</li><li>Two</li></ul><ul><li>One</li><li>Two</li></ul>Thirty-three bytes in, thirty-three bytes out. With nothing left to take, the run reports Already minified — saved 0 bytes.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
One space between words is content
A run of spaces, tabs and newlines between two words renders as one space, so it is kept everywhere — including next to an inline tag such as a link or a span, where dropping it glues two words together. Hence “Trim text boundaries” starts off.
Four elements no option can touch
pre, textarea, listing and plaintext render their whitespace literally, so their bodies are copied out byte for byte whatever you tick. script and style bodies too, unless you turn on an embedded-minifier switch; a title body is never touched at all. The four are the spec's own list, not whatever your stylesheet gives white-space: pre.
Three switches carry a rendering label
Trim text boundaries, remove optional end tags and remove attribute quotes each change bytes a browser reacts to, so all three default to off and wear a chip. The first warns as it works: “Space next to an inline element removed at line 1, column 4 — “Trim text boundaries” deletes space a browser renders as content — check the rendered words before shipping this output.”
Comments that do work are kept
Comment removal skips two shapes that are instructions rather than documentation: the legacy conditional comment opening <!--[if and the server-side include opening <!--#. A mixed head reports both at once — “1 comment removed.” beside “2 conditional or SSI comments kept — they carry behaviour, not documentation.”
Minified bytes and gzipped bytes answer different questions
A 9.8 KB indented pricing table falls to 7.7 KB here, 22% saved — yet gzipped the two weigh 924 B and 896 B, about 3% apart, because repeated indentation is what a compressor encodes for almost nothing. Those gzip figures come from Node's zlib; your browser will land a few bytes elsewhere.
Broken markup is described, never repaired
A mismatched end tag, an element left open, a slash on a non-void tag, a repeated attribute — each produces a positioned note and zero byte changes. Character references are untouched, so & stays &; the DOCTYPE and a leading byte-order mark ride through as written.
Common errors
`</div>` closes an element with `<span>` still open at line 1, column 14 — `<span>` is treated as ending here — no bytes are moved.- The tag stack disagreed with the document, so the run states its assumption instead of guessing at a repair. The tags are untouched.
`<div>` never closed at line 1, column 1 — the input ends with this element still open — it is reported, not repaired.- Pasting half a template is the usual cause. The offset points at the opening tag, not the end of the file — that is the line to fix.
This tool minifies up to 4MB of HTML; beyond that, split the document and run the parts separately.- Half a minified document is not the same document, so past the cap the answer is a refusal. No command line is suggested because none was verified on the machine that built this page.
FAQ
Can this change the way my page renders?
The two defaults touch only whitespace a browser discards under default styling, and comments that carry no behaviour. One exception: an element your CSS styles white-space: pre or pre-wrap does render that whitespace, and a minifier reading markup alone cannot see your stylesheet. Three optional switches change bytes outright, each labelled where you tick it.
Why is my <pre> block still full of spaces?
Because its whitespace is rendered: collapsing it would rewrite what a reader sees. Every byte between the tags is copied across, and the run counts the elements it protected.
Is minifying HTML worth it when the server already gzips?
Less than the raw percentage suggests: compressors handle repeated indentation cheaply. It still pays where bytes are counted before compression — inlined email markup, size caps on stored templates, documents served uncompressed.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Run the minifier — required past the large-input threshold.
- ⌘/Ctrl + S
- Download the minified markup as a file.
- ⌘/Ctrl + C
- With nothing selected, copy the output pane.
- ⌘/Ctrl + Shift + C
- Copy everything despite a selection.
- t
- Switch theme while no pane has focus.
- ? or ⌘/Ctrl + /
- Open the shortcut overlay.
- Esc
- Dismiss the top dialog; your input stays.