The same Markdown file can produce different HTML in your editor, your static site generator and your CMS. That is not a bug in any of them. Markdown was released in 2004 as a Perl script and a page of prose, with no grammar and no test suite, and for a decade the script was the specification. Everything since has been an attempt to pin down what it should have said.
What the Original Left Undecided
John Gruber's Markdown.pl came with a description of the syntax and a note that the implementation was the final word on behaviour. That worked while there was one implementation. Once there were dozens, every ambiguity became a fork.
The questions the original never answered are the ones that still bite. How many spaces indent a nested list? What does a list marker followed by nothing mean? Does emphasis apply inside a word? What happens when you open a bracket and never close it? Markdown.pl had an answer for each, but it was whatever the regular expressions happened to do, not a decision anyone made.
Jeff Atwood, who had hit the problem building Stack Overflow, put it plainly in 2012: there was no way to write a test suite because there was nothing to test against. The effort he started became CommonMark.
What CommonMark Settled
CommonMark is a specification with roughly 650 test cases, and its value is that it makes the awkward cases explicit rather than leaving them to the parser.
Emphasis is the clearest example. The spec devotes a long section and a formal algorithm to deciding which asterisks and underscores pair up, including a rule about delimiter runs whose lengths sum to a multiple of three. Nobody writes Markdown thinking about that rule, but it is why intraword_underscores_survive while *asterisks* do not, and why a naive parser mangles snake_case identifiers in prose.
List indentation got the same treatment. A nested item is indented to the column where the parent's content starts, not a fixed number of spaces, so a list under "10. item" needs four spaces where one under "- item" needs two. Most people learn this by getting it wrong.
CommonMark is now the base for GitHub, GitLab, Reddit, Discourse, Stack Overflow, and the major libraries in every language. It is the closest thing to a definition Markdown has.
Where the Flavours Actually Differ
Anything below the first two rows is an extension. If your document depends on one, it will render as literal text wherever that extension is not enabled.
| Feature | Support | Notes |
|---|---|---|
| Headings, lists, links, emphasis | Everywhere | The 2004 core, unchanged in substance since |
| Fenced code blocks | CommonMark | Backtick fences with a language label, now universal |
| Tables | GFM | Not in CommonMark; pipes render as literal pipes without the extension |
| Task lists | GFM | Checkbox syntax, rendered as disabled inputs |
| Strikethrough | GFM | Double tildes; single tildes work on GitHub but not everywhere |
| Bare URL autolinking | GFM | CommonMark requires angle brackets around the URL |
| Footnotes | Neither | Widely implemented, specified nowhere; pandoc and many SSGs add it |
| Front matter | Neither | A convention of static site generators, not part of any Markdown spec |
The Differences That Catch People
These four account for most of the moments where a document renders correctly in one place and wrong in another.
Single line breaks
In Markdown a newline inside a paragraph is just a space; a line break needs two trailing spaces or a backslash. GitHub comments, issues and pull request descriptions ignore that and break on every newline. Text written in an issue and pasted into a static site turns into one long paragraph.
Underscores inside words
CommonMark refuses to start emphasis inside a word with an underscore, precisely so that file_name_here survives. Older parsers italicise the middle. Asterisks have no such protection, which is why a literal asterisk in prose needs escaping.
Nested list indentation
Two spaces is the habit, but the rule is alignment with the parent item's content. Under an ordered list with two-digit numbers you need more. Get it wrong and the nested list becomes a paragraph inside the previous item, or a separate list entirely.
Raw HTML
CommonMark passes HTML in your source straight through to the output. Most hosted platforms do not: GitHub, Reddit and comment systems strip or escape it, because rendering user HTML on a shared page is how sites get exploited.
Markdown is not a sandbox
A Markdown parser is not a sanitiser, and the good ones say so in their own documentation. If raw HTML passes through, a script tag in the source becomes a script tag in the output. Even with HTML disabled, a link destination of javascript:… is legal Markdown and most parsers will emit it as an href. Anywhere you render Markdown that someone else wrote, the output has to go through a sanitiser such as DOMPurify before it reaches the page. This tool sanitises its own preview for exactly that reason, and leaves the HTML you copy untouched, because that document is yours.
Convert Markdown and see both sides
GFM tables and task lists, heading anchors, escaped or raw HTML.
Heading Anchors, and Why Yours Do Not Match
Nothing in any Markdown specification says a heading should get an id. Every platform that supports linking to a section invented its own rule, and the rules disagree.
GitHub lowercases the heading, removes punctuation, replaces spaces with hyphens, and appends -1, -2 to repeats. It keeps non-Latin characters as they are, so a Russian heading gets a Russian anchor that arrives percent-encoded when shared. Other generators transliterate instead, some strip stop words, and some number every heading from the start.
This matters when content moves. A table of contents generated for GitHub and published through a different pipeline can end up with links that point at anchors nobody generated. If your document links to its own headings, generate the anchors with the same rule as the destination, or write explicit ids in HTML and accept that the file is no longer portable.
Writing Markdown That Survives the Move
The practical advice is narrow, because most Markdown is fine anywhere. The parts that break are predictable.
Stay inside CommonMark plus GFM unless you control the renderer. Footnotes, definition lists and math blocks are useful and well supported in specific ecosystems, but they are extensions, and outside those ecosystems they render as the literal characters you typed.
Use blank lines between blocks, always. Almost every reported difference between renderers involves a block that starts on the line immediately after another one, and a blank line removes the ambiguity in every parser.
Avoid raw HTML in documents that will travel. It is the single feature most likely to be stripped, escaped, or rendered depending on where the file lands. If you need the structure, ask whether a table or a fenced block gets you close enough.
And check the output rather than trusting the input. A rendered preview alongside the source is the fastest way to catch the list that did not nest and the emphasis that swallowed a paragraph.
Frequently Asked Questions
Related Tools
Keep Reading
JSON Errors Explained: Trailing Commas, NaN and Other Rejections
Why valid-looking JSON fails to parse, how to read a parser's error position, and the number precision bug that silently corrupts large IDs.
URL Slugs: What Belongs in a Path and What Quietly Breaks It
Four punctuation marks are safe in a URL and the rest are not. What happens to accents once they leave the address bar, and why renaming costs more than it looks.
URL Encoding: encodeURI vs encodeURIComponent, and the Plus Sign
The two JavaScript functions are not interchangeable, and picking the wrong one is the most common URL bug there is. Plus where %2520 comes from.
How Diff Works: The Algorithm That Decides What Changed
Myers walks a grid one edit at a time, and Git has run it since 1986. Why the shortest diff is not always the clearest, and what makes one look wrong.