Guide · HTML → Markdown

HTML to Markdown

HTML can express far more than Markdown can. So this conversion is mostly a question of what to do with everything that has no equivalent.

Three answers, depending on the tag: map it, drop the tag and keep the text, or drop both. Which one applies is decided by a whitelist, and it's worth knowing what's on it.

Open the converter

A whitelist, not a blacklist

Only tags that mean something in Markdown survive the first pass: headings, paragraphs, lists, links, images, emphasis, blockquotes, code, tables, and the handful of inline tags around them. Everything else is removed, with its text kept.

The reason it's a whitelist is that a blacklist has to predict every dangerous tag, and HTML keeps adding new ones. One missed entry is a hole. This way the default answer for anything unfamiliar is no.

Scripts, event handlers and javascript: links go, and for script, style, iframe, object and embed the contents go too — not just the tag. Keeping the text of a <script> would paste its code into your document as visible prose.

Attributes are whitelisted the same way: only href, src, alt, title, colspan, rowspan and start. So class, id and style never reach the output. That's not only safety — Markdown has nowhere to put them.

Why sanitising matters even when nothing is rendered

This page never renders your HTML, so nothing can execute here. The reason the sanitiser exists is what happens next.

A link written as [click me](javascript:alert(1)) is copied through faithfully by a Markdown converter, and becomes a working attack the moment someone publishes that Markdown on a site that renders it. The risk isn't ours, it's handed to whoever uses the output.

So the URLs are checked against a protocol whitelist — http, https, mailto, ftp, and relative paths — and anything else is dropped. When something gets removed, the output says what it was, rather than quietly cleaning up your input behind your back.

Tables: keep them or flatten them

By default a table becomes a Markdown pipe table. Pipes inside cells are escaped, whitespace inside a cell is collapsed to single spaces, and short rows are padded to the width of the widest row so the table stays rectangular.

Flattening is the alternative, and it exists for tables that were never tables. A page laid out with a table for positioning converts to a pipe table full of empty cells; flattened, each row becomes a line of text with cells joined by a middle dot, which reads far better.

Two things don't survive either way. A <caption> is dropped, because a pipe table has nowhere to put one — copy it out as a line above the table if you need it. And block content inside a cell collapses: a list in a cell comes out as its items joined together, since a pipe table row has to be one line.

HTML
<table><tr><th>Part</th><th>Qty</th></tr>
<tr><td>Bolt | M6</td><td>12</td></tr></table>
Markdown
| Part | Qty |
| --- | --- |
| Bolt \| M6 | 12 |

The tags that keep their HTML

Superscript and subscript stay as <sup> and <sub>. Markdown has no syntax for them, and x2 instead of x² changes what a formula says — raw HTML is legal in Markdown and every renderer handles these two.

Underline doesn't get that treatment. It has no meaning to preserve: on the web an underline is a link, so keeping it would be actively misleading. Underlined text comes out as plain text.

Strikethrough becomes ~~, which is GitHub Flavoured Markdown rather than the original spec, but it's universal enough now that dropping it would be the stranger choice.

Lists, code blocks and the knobs

List items are written as "- item" with a single space. Most Markdown toolchains write it that way, and the common alternative — three spaces after the marker — makes for noisy diffs when a file is edited by both.

Nested lists indent to the width of the marker, and a paragraph continuing inside a list item is indented to line up with the text above it rather than breaking out of the list. An <ol> with a start attribute keeps its numbering.

The bullet character can be -, * or +, and the code fence can be ``` or ~~~. Pick to match whatever the file is going into; there's no functional difference. Headings can also be set to the underlined style, though only the first two levels have one — the third and below stay as # marks either way, which is worth knowing before you choose it.

HTML
<ol><li><p>First para</p>
<p>Still item one</p></li></ol>
Markdown
1. First para

   Still item one

Two inputs, one path

You can paste HTML source into the box, or drop an .html file. Both are treated identically, because to the code they're the same thing: a string of untrusted HTML.

The 25 MB cap is per input, which is far more than any page's source. Nothing is uploaded — the parsing, the sanitising and the conversion all happen in the tab.

If the result comes back empty, the output says so. Usually that means the input was all markup and no text: a page's <head>, or a fragment that was only styling.

Paste the source or drop the file, choose whether tables stay tables, and copy the Markdown out. It all runs in your browser.

HTML → MD