What a Markdown converter is allowed to trust
Published 2026-11-03 · Last reviewed 2026-11-06
Markdown looks like plain text, which is why everybody assumes a converter is a small, harmless thing. It is not: it is the step where text somebody else wrote becomes markup a browser will act on. Two decisions decide whether that step is safe. The first is which dialect is being read, because the same characters mean different things in GFM and in CommonMark. The second is what the output is allowed to contain, and that is a policy question rather than a parsing one.
This guide is about both, and about the one trick that lets a converter be checked rather than trusted: it writes the document back out and reads it again. On this site that check runs on every conversion, and the page says where the text changed if it ever does.
One model, two parsers, two writers
The converter holds one document model in the middle. Markdown is parsed into it, HTML is parsed into it, and two writers turn it back into either language. That is why the page can offer both directions without becoming two unrelated tools, and why the audit tables describe the same document whichever way it was read - 1 heading, 1 link and 1 table in the sample above, counted once in the model rather than twice in two implementations.
The model is also where the honesty lives. Elements Markdown cannot express are recorded as losses while the HTML is read, rather than being silently unwrapped; syntax the engine does not implement - footnotes, definition lists, maths, alert blockquotes - is reported as the text it will become, so it never quietly means something else.
Where GFM and CommonMark disagree
CommonMark fixes the syntax Markdown always had. GFM adds four things on top of it, and a reader that does not know they are switched on will print them as punctuation. With the sample above, GFM produces a real table and CommonMark produces 0 tables: the pipe table becomes a paragraph of text, and the pipes are content again. The switch is one select on the form, because the difference is a property of the reader, not of the document.
| Construct | CommonMark | GFM |
|---|---|---|
| Pipe tables | Text with pipes in it | A table with alignment |
| Task list items | A list item starting with brackets | A disabled checkbox |
| Strikethrough | Literal tildes | A del element |
| Bare links | Text that happens to look like a URL | An anchor |
| Indented code | Four spaces start a code block | This engine’s documented deviation: it does not |
A round trip is how a converter checks itself
Every conversion writes the document back out as Markdown and reads it with the same parser again. Then it compares the text, with runs of whitespace collapsed - not the structure. That choice is the point: structure is allowed to move around, because _emphasis_ may be written *emphasis* and an underlined heading may come back as an ATX one. A word is not allowed to change. For the sample above the read-back is identical, and the page would print the place where the two first disagree if it were not.
The check is about the converter, not about the visitor. A false result means an escape went wrong somewhere in this repository, and the finding says so in as many words. It also runs in the reverse direction: HTML is written out as Markdown, parsed back, and compared, so a table row or a list item that had to be re-shaped shows up as a changed line count rather than as a silent edit.
The trust boundary: escaping, schemes and images
Markdown from somebody else is untrusted input, and a converter is where untrusted input becomes markup, so the defaults are the conservative ones. Raw HTML is escaped unless a visitor explicitly asks for it: <span class="badge"> arrives asmarkup, which a reader can see and a browser cannot act on. A link whose scheme can run code is refused, with the line printed and the words kept - javascript:alert(1) leaves 1 problem and an anchor-free sentence, while the same page with the switch turned off keeps the destination exactly as written (kept).
The refusal runs on the decoded, whitespace-stripped address, because that is what a browser does: JaVaScRiPt:, javascript: and a tab inside the scheme all reach the same handler. An image is a different question from a link, so data:image/... is allowed to carry its pixels while a data: URL where a picture is expected is refused. And the page never draws the result: a preview would mean putting somebody else’s markup inside this page, which is exactly what the escaping exists to prevent.
Where these numbers come from
- Every count above is calculated, not copied in. Reading the sample, writing it back out in both directions, checking which scheme a link uses and comparing the round trip all follow the same conversion steps, and the figures in this guide are what those steps return for the sample shown. The Markdown to HTML Converterapplies the same steps to what a visitor types.
- Every release automatically checks each document: the round trip, the refusal of every scheme that can run code, the escaping of raw HTML, and the three downloads byte for byte. A separate check holds reading and writing to their own rules. The page's audit table, its report and its CSV are generated from the same findings the calculation returned, so the three cannot disagree.
- The two specifications are named rather than paraphrased. CommonMark is the syntax followed where the two overlap, and GFM is the one that adds the four constructs in the table; the two deviations this site documents - indented code and list continuation - are printed on the tool page beside every answer rather than hidden in a changelog.
What this guide does not cover
It does not claim to be a CommonMark implementation: the engine implements a named subset, and the page prints that subset instead of implying the rest. It does not cover sanitising HTML after the fact - the escaping here happens while the document is read, which is a different layer from a sanitiser that runs on a finished page. It does not tell you what a particular site will render: a host may strip tables, rewrite links or refuse inline images, and none of that is visible from this page. And it says nothing about the syntax the engine does not implement - footnotes, definition lists, maths - beyond reporting them as the text they will become.
Convert one in the Markdown to HTML Converter, tidy the result with the HTML Formatter, or count what the document says with the Word Counter.
Explanations only - not a security review of any particular site. Read the full disclaimer.